//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

misc.cpp

Abstract:


Notes:


--*/
/*
-------------------------------------------------------------------------------
    Revision History
    Date            Author             Activity ID               Activity Headline
    2006-04-22     qianyili           CEDB00015330             Move RIL from public to bsp270
    2006-05-12     qianyili           CEDB00015330             Enable loopback   
    2006-05-15     qianyili           CEDB00017192             Update a error of loopback  
    2007-01-09     Hanjianjun         CEDB200039056            Update ril driver for led  
    2007-01-09     sunrenhong         CEDB200039058            add cbc indication for orange
    2007-01-29     Hanjianjun         CEDB200042294            porting time synchoronization from shark.      
    2007-02-08     qinxuetao          CEDB200043651            Update ril driver for network and data service
    2007-03-19     zuoxiaona          CEDB200047228            Submit STK CODE -RIL- lampson
    2007-03-09     qinxuetao          CEDB200046239            Update ril driver to enable AT command test through extended ATCI
    2007-03-06     Hanjianjun         CEDB200045891            update ril driver for Led.      
    2007-03-19     Hanjianjun         CEDB200046536            update ril driver for lampson about seven color LEd.      
    2007-03-27     zhangshuo          CEDB200048981            Submit STK code to Lampson/delete some unnecessary notes.       
    2007-04-11     qinxuetao          CEDB200051438            Add +CPINC and +CPIN2
    2007-04-17     qinxuetao          WM600002434              Modified rilpm for Lampson.
    2007-04-19     qinxuetao          WM600002434              Modified rilpm for Lampson.
    2007-04-19     qinxuetao          WM600003234              Add audio channel control
    2007-04-26     qinxuetao          WM600002433              Add +TEDGE to get the access technology
    2007-04-29     hanjianjun         WM600003902              modified after resume the icon if battery cann't refresh.      
    2007-05-10     qinxuetao          WM600002914              Delay Dial commands to wait for radio to settle
    2007-05-10     qinxuetao          WM600002914              Modify audio channel control command to "+SPEAKER"
    2007-05-14     hanjianjun         WM600004068              modified time show error after reset.      
    2007-05-30     qinxuetao          WM600006448              On resuming, sending "AT+TCFG=\"IND\",255\r" to enable all indications, including "+CGREG"
    2007-06-07     qinxuetao          WM600003908              Can't enter STK main menu after bootup
    2007-06-15     qinxuetao          WM600004704              Change receiver loopback command for DF module
    2007-06-21     qinxuetao          WM600007125              Change STK and PB init procedure
    2007-06-22     qinxuetao          WM600007774              Modify STK input string encoding format, only UCS2 uses "HEX" format  
    2007-06-26     hanshuanghuan      WM600008296              Make STK application available even though radio has been turned off
    2007-06-28     Qinxuetao          WM600006133              Enable RIL supports PM IOCtrls interface 
    2007-07-13     qinxuetao          WM600009088              Set OEM default time on first boot or clean boot
    2007-07-26     qinxuetao          WM600011278              Using local time to synchronize between modem and PPC
    2007-08-02     qinxuetao          WM600009405              Add loopback parameter for melody playback
    2007-08-08     qinxuetao          WM600012587              Set RILDrv_EnablePowerSavings waiting timeout to 1s
    2007-08-21     qinxuetao          WM600014210              Get BT address from modem when device power on
    2007-08-31     zhongchuren        WM600015281              [FLY][AIS] all handset BT address is  ff:ff:ff:ff:ff:ff
    2008-01-17     Chenjianjun        WM600029490              Adding the SIM Lock function for Lampson.
-------------------------------------------------------------------------------
*/


#include "precomp.h"
#undef assert
#include <safeint.hxx>

#include <ndis.h>
#include <nuiouser.h>
#include <winuserm.h>


#ifdef RIL_ENABLE_AT_CHANNEL_FOR_ATCI
DWORD g_dwATCI = 0;
BOOL g_fATCILogOn = FALSE;
#endif

#ifdef RIL_ALLOW_UNICODE_PBENTRIES
#define ALLOW_UNICODE_PB_ENTRIES TRUE
#else
#define ALLOW_UNICODE_PB_ENTRIES FALSE
#endif

#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER)
extern BOOL g_bRadioOff;
#endif

extern RILLOCATIONINFO g_rliLocationInfo;
extern BOOL g_fInitialLocationInfoReceived;

//
// Equipment states
//
static const DWORD g_rgdwEquipmentStates[] =
{
    -1,     // RIL_EQSTATE_UNKNOWN
    0,      // RIL_EQSTATE_MINIMUM
    1,      // RIL_EQSTATE_FULL
    2,      // RIL_EQSTATE_DISABLETX
    3,      // RIL_EQSTATE_DISABLERX
    4,      // RIL_EQSTATE_DISABLETXANDRX
};
#define NUM_EQSTATES    (sizeof(g_rgdwEquipmentStates) / sizeof(DWORD))


//
// Phonebook storage locations
//
static const LPCSTR g_rgszPBLocations[] =
{
    "",             // RIL_PBLOC_UNKNOWN
    "EN",           // RIL_PBLOC_SIMEMERGENCY
    "FD",           // RIL_PBLOC_SIMFIXDIALING
    "LD",           // RIL_PBLOC_SIMLASTDIALING
    "ON",           // RIL_PBLOC_OWNNUMBERS
#ifdef OEM1_DRIVER
    "MT",           // RIL_PBLOC_SIMPHONEBOOK (MT is used for TI phonebook)
#else
    "SM",           // RIL_PBLOC_SIMPHONEBOOK   (Used by OEM2 and Wavecom)
#endif
};
#define NUM_PBLOCS     (sizeof(g_rgszPBLocations) / sizeof(LPCSTR))


//
// Bit error rates
//
static const DWORD g_rgdwBitErrorRates[] =
{
    14,     // 0
    28,     // 1
    57,     // 2
    113,    // 3
    226,    // 4
    453,    // 5
    905,    // 6
    1810,   // 7
};
#define NUM_BITERRORRATES     (sizeof(g_rgdwBitErrorRates) / sizeof(DWORD))


//
// Restricted SIM commands
//
static const DWORD g_rgdwRestrictedSIMCmds[] =
{
    -1,     // 0
    176,    // RIL_SIMCMD_READBINARY
    178,    // RIL_SIMCMD_READRECORD
    192,    // RIL_SIMCMD_GETRESPONSE
    214,    // RIL_SIMCMD_UPDATEBINARY
    220,    // RIL_SIMCMD_UPDATERECORD
    242,    // RIL_SIMCMD_STATUS
};
#define NUM_RESTRICTEDSIMCMDS   (sizeof(g_rgdwRestrictedSIMCmds) / sizeof(DWORD))


//
//
//
static const DWORD g_rgdwSIMToolkitTermCauses[] =
{
    -1,     // 0
    0,      // RIL_SIMTKITTERMCAUSE_USERSTOPPEDREDIAL
    1,      // RIL_SIMTKITTERMCAUSE_ENDOFREDIAL
    2,      // RIL_SIMTKITTERMCAUSE_USERENDEDSESSION
};
#define NUM_SIMTOOLKITTERMCAUSES   (sizeof(g_rgdwSIMToolkitTermCauses) / sizeof(DWORD))

extern HRESULT RILDrv_CheckBattery(DWORD dwParam,LPDETAILEDRILDEVSPECIFIC  devspsec);
extern  HRESULT ParseCheckBattery(LPCSTR szRsp, void*& pBlob, UINT& cbBlob);
HRESULT  RILDrv_NETWORKCOUNTS(DWORD dwParam, LPDETAILEDRILDEVSPECIFIC pDetailedDevSpecific);
HRESULT ParseSimLockCounts(LPCSTR szRsp,void*& pBlob,UINT& cbBlob);



/*-----------------------------------------------------------------------------------
FUNCTION:ParseGetEquipmentInfo()
PARAMETR:
LPCSTR  szRsp:  response string for pased.
void*     &pBlob:  this for stored pased data from szRsp
UINT      &cbBlob:  the length of pBlob
DESCRIPTION:
     none
--------------------------------------------------------------------------------------*/
HRESULT ParseGetEquipmentInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetEquipmentInfo);
    RILEQUIPMENTINFO* prei = NULL;
#ifdef RIL_WATSON_REPORT
    RILEQUIPMENTINFO* preiCache = &(g_RilInfoCache.rileiEquipmentInfo);
#endif // RIL_WATSON_REPORT
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prei = (RILEQUIPMENTINFO*)AllocBlob(sizeof(RILEQUIPMENTINFO));
    if (!prei) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prei, 0x00, sizeof(RILEQUIPMENTINFO));
    prei->cbSize = sizeof(RILEQUIPMENTINFO);

#ifdef RIL_WATSON_REPORT
    // Clear equipment info cache
    memset(preiCache, 0x00, sizeof(RILEQUIPMENTINFO));
    preiCache->cbSize = sizeof(RILEQUIPMENTINFO);
#endif // RIL_WATSON_REPORT

    // Parse "<prefix><manufacturer_id><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                       ||
        !ParseUnquotedString(szRsp, '\r', prei->szManufacturer, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_MANUFACTURER;

#ifdef RIL_WATSON_REPORT
    // Copy to cache
    strncpy(preiCache->szManufacturer, prei->szManufacturer, MAXLENGTH_EQUIPINFO);
    preiCache->dwParams |= RIL_PARAM_EI_MANUFACTURER;
#endif // RIL_WATSON_REPORT

    // Parse "<prefix><model_id><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                ||
        !ParseUnquotedString(szRsp, '\r', prei->szModel, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_MODEL;

#ifdef RIL_WATSON_REPORT
    // Copy to cache
    strncpy(preiCache->szModel, prei->szModel, MAXLENGTH_EQUIPINFO);
    preiCache->dwParams |= RIL_PARAM_EI_MODEL;
#endif // RIL_WATSON_REPORT

    // Parse "<prefix><revision_id><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                   ||
        !ParseUnquotedString(szRsp, '\r', prei->szRevision, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_REVISION;

#ifdef RIL_WATSON_REPORT
    // Copy to cache
    strncpy(preiCache->szRevision, prei->szRevision, MAXLENGTH_EQUIPINFO);
    preiCache->dwParams |= RIL_PARAM_EI_REVISION;
#endif // RIL_WATSON_REPORT

    // Parse "<prefix><serial_number><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                                                       ||
        !ParseUnquotedString(szRsp, '\r', prei->szSerialNumber, MAXLENGTH_EQUIPINFO, szRsp) ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prei->dwParams |= RIL_PARAM_EI_SERIALNUMBER;

#ifdef RIL_WATSON_REPORT
    // Copy to cache
    strncpy(preiCache->szSerialNumber, prei->szSerialNumber, MAXLENGTH_EQUIPINFO);
    preiCache->dwParams |= RIL_PARAM_EI_SERIALNUMBER;
#endif // RIL_WATSON_REPORT

    pBlob = (void*)prei;
    cbBlob = sizeof(RILEQUIPMENTINFO);

Error:
    if (FAILED(hr)) {
        FreeBlob(prei);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetEquipmentInfo(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetEquipmentInfo);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef OEM1_DRIVER
    if (!QueueCmd(pHandle, "AT+CGMI;+CGMM;+CGMR;D*#06#\r", CMDOPT_NONE, APIID_GETEQUIPMENTINFO,
#else
    if (!QueueCmd(pHandle, "AT+CGMI;+CGMM;+CGMR;+CGSN\r", CMDOPT_NONE, APIID_GETEQUIPMENTINFO,
#endif // OEM1_DRIVER
                  ParseGetEquipmentInfo, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
// Helper function to fill RILEQUIPMENTSTATE structure and implement
// driver-specific RIL_RADIOSUPPORT_* mapping.
//
void FillEquipmentStateStruct(RILEQUIPMENTSTATE * pres, DWORD dwEqState)
{
    DEBUGCHK(NULL != pres);

    memset(pres, 0x00, sizeof(RILEQUIPMENTSTATE));

    pres->cbSize = sizeof(RILEQUIPMENTSTATE);
    pres->dwParams = RIL_PARAM_EQUIPMENTSTATE_ALL;
    pres->dwEqState = dwEqState;
    pres->dwReadyState = g_dwReadyState;
    if (RIL_EQSTATE_UNKNOWN == dwEqState)
    {
        pres->dwRadioSupport = RIL_RADIOSUPPORT_UNKNOWN;
    }
    else
    {
        pres->dwRadioSupport = (dwEqState == RIL_EQSTATE_FULL) ? RIL_RADIOSUPPORT_ON : RIL_RADIOSUPPORT_OFF;
    }
}

//
//
//
HRESULT ParseGetEquipmentState(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetEquipmentState);
    UINT nValue;
    DWORD dwState;
    RILEQUIPMENTSTATE * pres = NULL;
    UINT i;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pres = (RILEQUIPMENTSTATE*)AllocBlob(sizeof(RILEQUIPMENTSTATE));
    if (!pres) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    // Parse "<prefix>+CFUN: <state><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CFUN: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
//For wavecom driver, mapping airplane mode to phone off mode
#ifdef WAVECOM_DRIVER
    if(nValue == 3)
    {
        nValue = 0;
    }
#endif

    for (i = 0; i < NUM_EQSTATES; i++) {
        if (nValue == g_rgdwEquipmentStates[i]) {
            dwState = i;
            break;
        }
    }
    if (NUM_EQSTATES == i) {
        // We couldn't match the response with anything
        dwState = RIL_EQSTATE_UNKNOWN;
    }

    FillEquipmentStateStruct(pres, dwState);

    pBlob = (void*)pres;
    cbBlob = pres->cbSize;

#ifdef OEM2_DRIVER
    g_bRadioOff = dwState == RIL_EQSTATE_MINIMUM;
#endif
Error:
    if (FAILED(hr)) {
        FreeBlob(pres);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetEquipmentState(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetEquipmentState);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(pHandle, "AT+CFUN?\r", CMDOPT_IGNORERADIOOFF, APIID_GETEQUIPMENTSTATE, ParseGetEquipmentState, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetEquipmentState(DWORD dwParam, DWORD dwEquipmentState)
{
    FUNCTION_TRACE(RILDrv_SetEquipmentState);
    DEBUGCHK(NUM_EQSTATES > dwEquipmentState);

    DWORD dwCmdOpt = CMDOPT_NONE;

    char szCmd[MAX_PATH];
    HRESULT hr = S_OK;
    CNotificationData * pnd = NULL;
    RILEQUIPMENTSTATE res;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Don't allow equipment states that would overrun the array.
    if ( dwEquipmentState >= NUM_EQSTATES )
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    if (RIL_EQSTATE_UNKNOWN == dwEquipmentState) {
        hr = E_INVALIDARG;
        goto Error;
    }
#ifdef EMP_DRIVER
    if (RIL_EQSTATE_DISABLETX == dwEquipmentState || RIL_EQSTATE_DISABLERX == dwEquipmentState)
    {
        hr = E_NOTIMPL;
        goto Error;
    }
#endif

    FillEquipmentStateStruct(&res, dwEquipmentState);
    if (res.dwRadioSupport == RIL_RADIOSUPPORT_OFF)
    {
        // We'll be turning the radio off
        res.dwReadyState = 0;
        g_dwGPRSRegStatus = RIL_REGSTAT_UNKNOWN;
        g_dwRegStatus = RIL_REGSTAT_UNKNOWN;

#ifdef RIL_WATSON_REPORT
        // Reset registration status in info cache.
        g_RilInfoCache.dwRegStatus = RIL_REGSTAT_UNKNOWN;
        g_RilInfoCache.dwGPRSRegStatus = RIL_REGSTAT_UNKNOWN;
#endif // RIL_WATSON_REPORT

    }

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED, (void*)&res, res.cbSize))
    {
        delete pnd;
        pnd = NULL;
    }

    // If this command will turn on the radio, then
    // note this in the command option. If this option
    // is not set for the SETEQUIPMENTSTATE command,
    // then it is assumed that it turns off the radio.
    if (RIL_RADIOSUPPORT_ON == res.dwRadioSupport)
    {
        dwCmdOpt |= CMDOPT_SETRADIOON;

#if defined(WAVECOM_DRIVER) || defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER)
        // HW-SPECIFIC: WaveCom hardware always resets after AT+CFUN=<n>

        dwCmdOpt |= CMDOPT_REINIT;
#endif // WAVECOM_DRIVER
#if defined(OEM2_DRIVER) || defined(EMP_DRIVER)
       if (FALSE ==  g_bRadioOff)
       {
            // The radio is already on, no need to turn it on again, particularly since we won't get the CPINN
            // notification again, and this will cause the command thread to time out waiting for it.
            dwCmdOpt |= CMDOPT_NOOP;
       }
#endif

    }

    // This command is OK to send, even if the radio is "off".
    dwCmdOpt |= CMDOPT_IGNORERADIOOFF;

    // Set a registry key to save the new state of the radio in a location that persists across rebooting
    (void)SetRegistryDWORD(RIL_LAST_EQUIPMENT_STATE_ROOT, RIL_LAST_EQUIPMENT_STATE_KEY, RIL_LAST_EQUIPMENT_STATE_VALUE, dwEquipmentState);

#ifdef WAVECOM_DRIVER
    if (dwCmdOpt & CMDOPT_SETRADIOON)
    {  
        (void)_snprintfz(szCmd, MAX_PATH, "AT+CFUN=%u\r", g_rgdwEquipmentStates[dwEquipmentState]);
    }
    else
#elif defined(RIL_ENABLE_SIMTK) && defined(OEM1_DRIVER)
    if (dwCmdOpt & CMDOPT_SETRADIOON)
    {
        // We must send the terminal profile string along when we turn on the radio
        (void)_snprintfz(szCmd, MAX_PATH, "AT%%SATC=1,0901FF7F00000010;+CFUN=%u\r", g_rgdwEquipmentStates[dwEquipmentState]);
    }
    else
#endif
    {
#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
        if(RIL_EQSTATE_MINIMUM == dwEquipmentState)
        {
            // When turning the radio off, first force a GPRS detach in case the radio doesn't do so automatically
            DEBUGCHK(0 == g_rgdwEquipmentStates[dwEquipmentState]);
#ifdef OEM1_DRIVER
            (void)_snprintfz(szCmd, MAX_PATH, "AT+CFUN=0\r");
#elif defined(WAVECOM_DRIVER)
            (void)_snprintfz(szCmd, MAX_PATH, "AT+CFUN=3\r");
#else
            (void)_snprintfz(szCmd, MAX_PATH, "AT+CGATT=0;+CFUN=0\r");
#endif // OEM1_DRIVER
        }
        else
#endif // defined(OEM1_DRIVER) || defined(OEM2_DRIVER)
        {
            // Generic +CFUN command
            (void)_snprintfz(szCmd, MAX_PATH, "AT+CFUN=%u\r", g_rgdwEquipmentStates[dwEquipmentState]);
        }
    }

    if (!QueueCmd(pHandle, szCmd, dwCmdOpt, APIID_SETEQUIPMENTSTATE, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT ParseGetPhonebookOptions(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetPhonebookOptions);
    UINT i;
    UINT nValue;
    char szLocation[MAX_PATH];
    RILPHONEBOOKINFO* prpbi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prpbi = (RILPHONEBOOKINFO*)AllocBlob(sizeof(RILPHONEBOOKINFO));
    if (!prpbi) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prpbi, 0x00, sizeof(RILPHONEBOOKINFO));
    prpbi->cbSize = sizeof(RILPHONEBOOKINFO);

    // Parse "<prefix>+CPBS: <storage>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CPBS: ", szRsp) ||
        !ParseString(szRsp, szLocation, MAX_PATH, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    for (i = 0; i < NUM_PBLOCS; i++) {
        if (!strcmp(szLocation, g_rgszPBLocations[i])) {
            prpbi->dwStoreLocation = i;
            break;
        }
    }
    if (NUM_PBLOCS == i) {
        // We couldn't match the response with anything
        prpbi->dwStoreLocation = RIL_PBLOC_UNKNOWN;
    }
    prpbi->dwParams |= RIL_PARAM_PBI_STORELOCATION;

    // Parse ","
    if (MatchStringBeginning(szRsp, ",", szRsp)) {
        // Parse "<used>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        prpbi->dwUsed = nValue;
        prpbi->dwParams |= RIL_PARAM_PBI_USED;

#ifdef EMP_DRIVER
        // EMP may return 510, which means that <used> is unavailable
        if (510 == nValue)
        {
            prpbi->dwParams &= ~RIL_PARAM_PBI_USED;
        }
#endif

        // Parse ",<total>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
#if defined(OEM1_DRIVER)
        // This hardware seems to have a problem reading the 255th entry
        // in the SIM phone book (it crashes).  Therefore, do not report the
        // existance of this entry to the caller.
        if (255 == nValue) {
            --nValue;
        }
#endif
        prpbi->dwTotal = nValue;
        prpbi->dwParams |= RIL_PARAM_PBI_TOTAL;
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prpbi;
    cbBlob = sizeof(RILPHONEBOOKINFO);

Error:
    if (FAILED(hr)) {
        FreeBlob(prpbi);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetPhonebookOptions(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetPhonebookOptions);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(pHandle, "AT+CPBS?\r", CMDOPT_NONE, APIID_GETPHONEBOOKOPTIONS, ParseGetPhonebookOptions, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetPhonebookOptions(DWORD dwParam, const RILPHONEBOOKINFO* lpPhonebookInfo)
{
    FUNCTION_TRACE(RILDrv_SetPhonebookOptions);
    CNotificationData* pnd = NULL;
    char szCmd[MAX_PATH];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpPhonebookInfo) {
        hr = E_FAIL;
        goto Error;
    }

    // Store location must be specified
    if (!(lpPhonebookInfo->dwParams & RIL_PARAM_PBI_STORELOCATION)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Store location index must be in the valid range
    DEBUGCHK(NUM_PBLOCS > lpPhonebookInfo->dwStoreLocation);
    if (RIL_PBLOC_UNKNOWN == lpPhonebookInfo->dwStoreLocation) {
        hr = E_INVALIDARG;
        goto Error;
    }

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_PHONEBOOKSTORAGECHANGED, lpPhonebookInfo->dwStoreLocation)) {
        delete pnd;
        pnd = NULL;
    }
    (void)_snprintfz(szCmd, MAX_PATH, "AT+CPBS=\"%s\"\r", g_rgszPBLocations[lpPhonebookInfo->dwStoreLocation]);
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETPHONEBOOKOPTIONS, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

#ifdef RIL_RADIO_RESILIENCE
    g_fPBLocationSet = TRUE;
    (void)strncpyz(g_szPBLocCmd, szCmd, ARRAYSIZE(g_szPBLocCmd));
#endif // RIL_RADIO_RESILIENCE

Error:
    return hr;
}

#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER)
static void ConvertDTMFControlDigitSeparatorCharacter(LPWSTR szAddress)
{
    ASSERT(NULL != szAddress);
    WCHAR ch;
    while (L'\0' != (ch = *szAddress))
    {
        if (L'W' == ch || L'w' == ch)
            *szAddress = L'p';

        ++szAddress;
    }
}
#endif

//
//
//
static HRESULT ParseArrayOfPhonebookEntries(LPCSTR szCmd, LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseArrayOfPhonebookEntries);
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    char szAddress[MAXLENGTH_ADDRESS];
    WCHAR wszText[MAXLENGTH_PHONEBOOKTEXT];
    RILPHONEBOOKENTRY* rgrbpe = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<command>"
    while (MatchStringBeginning(szRsp, szCmd, szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrbpe, sizeof(RILPHONEBOOKENTRY), nUsed, &nAllocated, MISC_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrbpe[nUsed], 0x00, sizeof(RILPHONEBOOKENTRY));
        rgrbpe[nUsed].cbSize = sizeof(RILPHONEBOOKENTRY);

        // Parse "<index>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        rgrbpe[nUsed].dwIndex = nValue;
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_INDEX;

        // clear buffer before reusing
        memset(szAddress, 0x00, sizeof(szAddress));

        // Parse ",<number>,<type>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)     ||
            !MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) ||
            !StringToRILAddress(szAddress, (BYTE)nValue, rgrbpe[nUsed].raAddress)) {
            goto Continue;
        }
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_ADDRESS;
#if defined (OEM1_DRIVER)|| defined(OEM2_DRIVER) || defined(EMP_DRIVER)
        ConvertDTMFControlDigitSeparatorCharacter(rgrbpe[nUsed].raAddress.wszAddress);
#endif

        // clear buffer before reusing
        memset(wszText, 0x00, sizeof(wszText));

        // Parse ",<text>"
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseQuotedEncodedString(ENCODING_TECHARSET, szRsp, wszText, wszText + MAXLENGTH_PHONEBOOKTEXT)) {
            goto Continue;
        }
        (void)wcsncpyz(rgrbpe[nUsed].wszText, wszText, MAXLENGTH_PHONEBOOKTEXT);
        rgrbpe[nUsed].dwParams |= RIL_PARAM_PBE_TEXT;

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)rgrbpe;
    cbBlob = nUsed * sizeof(RILPHONEBOOKENTRY);

Error:
    if (FAILED(hr)) {
        delete[] (BYTE*)rgrbpe;
    }
    return hr;
}


//
//
//
static HRESULT ParseReadPhonebookEntries(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseReadPhonebookEntries);
    return ParseArrayOfPhonebookEntries("+CPBR: ", szRsp, pBlob, cbBlob);
}


//
//
//
HRESULT RILDrv_ReadPhonebookEntries(DWORD dwParam, DWORD dwStartIndex, DWORD dwEndIndex)
{
    FUNCTION_TRACE(RILDrv_ReadPhonebookEntries);
    char szCmd[MAX_PATH];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if(dwStartIndex == dwEndIndex)
    {
        (void)_snprintfz(szCmd, MAX_PATH, "AT+CPBR=%u\r", dwStartIndex);
        if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_READPHONEBOOKENTRIES, ParseReadPhonebookEntries, NULL, hr))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        (void)_snprintfz(szCmd, MAX_PATH, "AT+CPBR=%u,%u\r", dwStartIndex, dwEndIndex);
        //  roughly over 100 entries read in 10 secs based on current empirical data
        unsigned Timeout = 0;  //  in msec
        DWORD PbEntries = dwEndIndex - dwStartIndex;
        if ( (dwEndIndex > dwStartIndex) && (PbEntries > 100) &&
             !safeIntUMul( PbEntries, 100, &Timeout ) )
        {
            Timeout = UINT_MAX;
        }
        if ( !QueueCmdWithTimeout( pHandle, szCmd, NULL, CMDOPT_NONE, APIID_READPHONEBOOKENTRIES,
                               ParseReadPhonebookEntries, NULL, hr, Timeout, 0 ) ) 
        {
            hr = E_FAIL;
            goto Error;
        } 
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_WritePhonebookEntry(DWORD dwParam, const RILPHONEBOOKENTRY* lpEntry)
{
    FUNCTION_TRACE(RILDrv_WritePhonebookEntry);
    CNotificationData* pnd = NULL;
    BYTE bTypeOfAddress;
    char szAddress[MAXLENGTH_ADDRESS];
    char szCmd[MISC_CMDBUF_LENGTH_PB];
    LPSTR szWalk = szCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpEntry) {
        hr = E_FAIL;
        goto Error;
    }

    if (!(lpEntry->dwParams & RIL_PARAM_PBE_INDEX) || !(lpEntry->dwParams & RIL_PARAM_PBE_ADDRESS)) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Determine address and type-of-address byte
    hr = RILAddressToString(lpEntry->raAddress, szAddress, MAXLENGTH_ADDRESS, bTypeOfAddress);
    if (FAILED(hr)) {
        goto Error;
    }

    if (RIL_PBINDEX_FIRSTAVAILABLE == lpEntry->dwIndex) {
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd), "AT+CPBW=,\"%s\",%u", szAddress, bTypeOfAddress);
    } else {
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd), "AT+CPBW=%u,\"%s\",%u", lpEntry->dwIndex, szAddress,
                         bTypeOfAddress);
    }
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    // Add text, if supplied
    if (lpEntry->dwParams & RIL_PARAM_PBE_TEXT) {
        (void)strncpyz(szWalk, ",", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        if (!AppendQuotedEncodedString(ENCODING_TECHARSET, lpEntry->wszText, szWalk, (PUCHAR)&szCmd[MISC_CMDBUF_LENGTH_PB], ALLOW_UNICODE_PB_ENTRIES))
        {
            goto Error;
        }

    }
    (void)strncpyz(szWalk, "\r", MISC_CMDBUF_LENGTH_PB - (szWalk - szCmd));  // NO_TYPO: 30

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_PHONEBOOKENTRYSTORED, lpEntry->dwIndex)) {
        delete pnd;
        pnd = NULL;
    }
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_WRITEPHONEBOOKENTRY, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_DeletePhonebookEntry(DWORD dwParam, DWORD dwIndex)
{
    FUNCTION_TRACE(RILDrv_DeletePhonebookEntry);
    CNotificationData* pnd = NULL;
    char szCmd[MAX_PATH];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromDWORDBlob(RIL_NOTIFY_PHONEBOOKENTRYDELETED, dwIndex)) {
        delete pnd;
        pnd = NULL;
    }
    (void)_snprintfz(szCmd, MAX_PATH, "AT+CPBW=%u\r", dwIndex);
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_DELETEPHONEBOOKENTRY, NULL, pnd, hr)) {
        hr = E_FAIL;
        goto Error;
    }
    pnd = NULL;

Error:
    return hr;
}


//
//
//
static HRESULT ParseSendSimCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseSendSimCmd);
    UINT i;
    UINT nValue = 0;
    UINT cbResponse = 0;
    BYTE* pbResponse = NULL;
    BYTE* pbResponseWalk;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>+CSIM: <length>,"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CSIM: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    DEBUGCHK(0 == nValue % 2);

    if (0 < nValue) {
        // Allocate the storage
        cbResponse = nValue / 2;
        pbResponse = (BYTE *)AllocBlob(cbResponse);
        if (!pbResponse) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }

        pbResponseWalk = pbResponse;
        for (i = 0; i < cbResponse; i++) {
            if (!*szRsp || !*(szRsp + 1)) {
                hr = E_FAIL;
                goto Error;
            }
            *pbResponseWalk = SemiByteCharsToByte(*szRsp, *(szRsp + 1));
            pbResponseWalk++;
            szRsp += 2;
        }
        // Parse "<postfix>"
        if (!ParseRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        // Special case where the response was 0 length.
        // Handle it by trashing the rest of the response.
        DEBUGCHK(FALSE);
        if (!MatchStringAnywhere(szRsp, "\r\n", szRsp))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

    pBlob = (void*)pbResponse;
    cbBlob = cbResponse;

Error:
    if (FAILED(hr)) {
        FreeBlob(pbResponse);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_SendSimCmd(DWORD dwParam, const BYTE* lpbCommand, DWORD dwSize)
{
    FUNCTION_TRACE(RILDrv_SendSimCmd);
#if defined(OEM1_DRIVER)
    // HW-SPECIFIC: WaveCom does not support AT+CSIM

    DEBUGCHK(NULL != lpbCommand);
    DEBUGCHK(0 != dwSize);

    LPSTR szCmd = NULL;
    char szPrefix[MAX_PATH];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpbCommand) {
        hr = E_FAIL;
        goto Error;
    }

    // Add "AT+CSIM=<length>," to prefix string
    (void)_snprintfz(szPrefix, MAX_PATH, "AT+CSIM=%u,", dwSize * 2);

    // Add "<prefix>\"<command>\"<CR>"
    // NOTE: we take ownership of allocated szCmd
    if (!ComposeCmdWithByteArray(szPrefix, lpbCommand, dwSize, "\r", szCmd)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE | CMDOPT_SUPPRESSLOGGING, APIID_SENDSIMCMD, ParseSendSimCmd, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    delete szCmd;
#else  // defined(OEM1_DRIVER)

    HRESULT hr = E_NOTIMPL;
#endif // defined(OEM1_DRIVER)

    return hr;
}


//
//
//
HRESULT ParseATRInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseATRInfo);

    HRESULT hr = S_OK;
    RILATRINFO* pATRInfo = NULL;
    UINT uiPhase;
    UINT uiCurATRChar;
    UINT cbATRString;
    UINT cbATRData;
    LPSTR szATRString = NULL;
    LPSTR pchATRStringWalk;
    BYTE* pbATRDataWalk;


    // Check parameters
    if (NULL == szRsp)
    {
        DEBUGCHK(FALSE);
        return E_INVALIDARG;
    }

    // Initialize output parameters
    pBlob = NULL;
    cbBlob = 0;

    pATRInfo = (RILATRINFO*)AllocBlob(sizeof(RILATRINFO));
    if (NULL == pATRInfo)
    {
        DEBUGCHK(FALSE);
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    memset(pATRInfo, 0, sizeof(RILATRINFO));
    pATRInfo->cbSize = sizeof(RILATRINFO);

    // Parse "<prefix>%ATR: <Phase>,<ATR>"
    // We take ownership of szATRString and are responsible for freeing the memory.
    if (!ParseRspPrefix(szRsp, szRsp)                 ||
        !MatchStringBeginning(szRsp, "%ATR: ", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, uiPhase, szRsp)    ||
        !MatchStringBeginning(szRsp, ",", szRsp)      ||
        !ParseUnlimitedUnquotedString(szRsp, '\r', szATRString, cbATRString, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Assert that the data we received is an even length.
    DEBUGCHK(0 == (cbATRString-1) % 2);

    cbATRData = (cbATRString-1) / 2;
    pchATRStringWalk = szATRString;
    pbATRDataWalk = pATRInfo->rgbATR;

    // Check that the data is not larger than the space available in the structure.
    if (cbATRData > ARRAY_LENGTH(pATRInfo->rgbATR))
    {
        DEBUGCHK(!"ATR Data is larger than the structure buffer.");
        hr = E_FAIL;
        goto Error;
    }

    // Walk through the ATR string and convert to bytes.
    for (uiCurATRChar = 0 ; uiCurATRChar < cbATRData ; uiCurATRChar++)
    {
        *pbATRDataWalk = SemiByteCharsToByte(*pchATRStringWalk, *(pchATRStringWalk + 1));
        pbATRDataWalk++;
        pchATRStringWalk += 2;
    }


    // Everything is parsed, finish setting all the fields of the structure.
    pATRInfo->cbATRSize = cbATRData;
    pATRInfo->dwPhase = uiPhase;
    pATRInfo->dwParams = RIL_PARAM_ATR_ALL;

    // Everything is finished. Assign the results to the output parameters
    pBlob = pATRInfo;
    cbBlob = sizeof(RILATRINFO);

Error:
    if (FAILED(hr))
    {
        FreeBlob(pATRInfo);
    }
    return hr;
    return S_OK;
}


//
//
//
HRESULT RILDrv_GetATR(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetATR);
#if defined(OEM1_DRIVER)
    HRESULT hr = S_OK;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if(!QueueCmd(pHandle, "AT%ATR?\r", CMDOPT_IGNORERADIOOFF | CMDOPT_SUPPRESSLOGGING, APIID_GETATR, ParseATRInfo, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

Error:
#else  // !defined(OEM1_DRIVER)

    HRESULT hr = E_NOTIMPL;
#endif // defined(OEM1_DRIVER)

    return hr;
}


//
//
//
HRESULT ParseSendRestrictedSimCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseSendRestrictedSimCmd);
    UINT i;
    UINT nSW1;
    UINT nSW2;
    LPSTR szResponseString = NULL;
    UINT cbResponseString;
    UINT cbStruct;
    BYTE* pbResponseWalk;
    LPSTR pchResponseStringWalk;
    RILSIMRESPONSE* prsr = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;
    ASSERT(szRsp);
    // Parse "<prefix>+CRSM: <sw1>,<sw2>"
    if (!szRsp ||
        !ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CRSM: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nSW1, szRsp)           ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nSW2, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        // No response data present
        cbStruct = sizeof(RILSIMRESPONSE);
    } else {
        // Parse "<response>"
        // NOTE: we take ownership of allocated szResponseString
#if defined(OEM2_DRIVER) || defined(EMP_DRIVER)
        if (!ParseUnlimitedString(szRsp, szResponseString, cbResponseString, szRsp)) {
#else
        if (!ParseUnlimitedUnquotedString(szRsp, '\r', szResponseString, cbResponseString, szRsp)) {
#endif
            hr = E_FAIL;
            goto Error;
        }
        DEBUGCHK(0 == (cbResponseString - 1) % 2);
        cbStruct = sizeof(RILSIMRESPONSE) + (cbResponseString - 1) / 2;
    }

    // Allocate the structure of needed size
    prsr = (RILSIMRESPONSE*)AllocBlob(cbStruct);
    if (!prsr) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsr, 0x00, cbStruct);
    prsr->dwParams = (RIL_PARAM_SR_STATUSWORD1 | RIL_PARAM_SR_STATUSWORD2);

    // Decode the reponse data, if present
    if (sizeof(RILSIMRESPONSE) < cbStruct) {
        pbResponseWalk = prsr->pbResponse;
        pchResponseStringWalk = szResponseString;
        for (i = 0; i < cbStruct - sizeof(RILSIMRESPONSE); i++) {
            *pbResponseWalk = SemiByteCharsToByte(*pchResponseStringWalk, *(pchResponseStringWalk + 1));
            pbResponseWalk++;
            pchResponseStringWalk += 2;
        }
        prsr->dwParams |= RIL_PARAM_SR_RESPONSE;
    }

    prsr->cbSize = cbStruct;
    prsr->dwStatusWord1 = nSW1;
    prsr->dwStatusWord2 = nSW2;

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)prsr;
    cbBlob = cbStruct;

Error:
    if (FAILED(hr)) {
        FreeBlob(prsr);
    }
    delete[] szResponseString;
    return hr;
}

#ifdef WAVECOM_DRIVER
#define INDEX_OF_PIN      (18)
#define INDEX_OF_PUK      (19)
#define INDEX_OF_PIN2     (20)
#define INDEX_OF_PUK2     (21)
#define CRSM_DATA_LENGTH  (22)
HRESULT ParseQueryPINCounter(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParsePasswordTimesQuery);
    UINT nPIN = 0;
    UINT nPUK = 0;
    UINT nPIN2 = 0;
    UINT nPUK2 = 0;
    UINT nSW1 = 144;
    UINT nSW2 = 0;
    UINT cbStruct = 0;
    RILSIMRESPONSE* prsr = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;
 
    // Parse "<prefix>+CPINC: <nPIN>,<nPIN2>,<nPUK>,<nPUK2><postfix>"
    if (!szRsp ||
        !ParseRspPrefix(szRsp, szRsp)  ||
        !MatchStringBeginning(szRsp, "+CPINC: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nPIN, szRsp)           ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nPIN2, szRsp)           ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nPUK, szRsp)          ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nPUK2, szRsp)          ||
        !ParseRspPostfix(szRsp, szRsp) )
    {
        hr = E_FAIL;
        goto Error;
    }

    // Allocate the structure of needed size
    cbStruct = sizeof(RILSIMRESPONSE) + CRSM_DATA_LENGTH;
    prsr = (RILSIMRESPONSE*)AllocBlob(cbStruct);
    if (!prsr) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsr, 0x00, cbStruct);
    prsr->cbSize = cbStruct;
    prsr->dwParams = (RIL_PARAM_SR_STATUSWORD1 | RIL_PARAM_SR_STATUSWORD2);
    prsr->dwStatusWord1 = nSW1;
    prsr->dwStatusWord2 = nSW2;
    
    prsr->pbResponse[INDEX_OF_PIN] = nPIN;
    prsr->pbResponse[INDEX_OF_PUK] = nPUK;
    prsr->pbResponse[INDEX_OF_PIN2] = nPIN2;
    prsr->pbResponse[INDEX_OF_PUK2] = nPUK2;
    prsr->dwParams |= RIL_PARAM_SR_RESPONSE;
    
    pBlob = (void*)prsr;
    cbBlob = cbStruct;

Error:
    if (FAILED(hr)) 
    {
        FreeBlob(prsr);
    }
    return hr;
}
#endif
//
//
//
HRESULT RILDrv_SendRestrictedSimCmd(DWORD dwParam, DWORD dwCommand, const RILSIMCMDPARAMETERS* lpParameters,
                                    const BYTE* lpbData, DWORD dwSize)
{
    FUNCTION_TRACE(RILDrv_SendRestrictedSimCmd);

#ifndef WAVECOM_DRIVER
    // HW-SPECIFIC: WaveCom hardware doesn't support AT+CRSM

    DEBUGCHK(0 != dwCommand);
    DEBUGCHK(NUM_RESTRICTEDSIMCMDS > dwCommand);

    LPSTR szCmd = NULL;
    char szPrefix[MAX_PATH];
    LPSTR szPrefixWalk = szPrefix;
    UINT cbDataSizeToUse = 0;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpbData) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef EMP_DRIVER
    // EMP does not support RIL_SIMCMD_STATUS
    if (RIL_SIMCMD_STATUS == dwCommand)
    {
        hr = E_NOTIMPL;
        goto Error;
    }
#endif

    // Add "AT+CRSM=<command>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_PATH - (szPrefixWalk - szPrefix), "AT+CRSM=%u", g_rgdwRestrictedSIMCmds[dwCommand]);
    szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szPrefixWalk);

    if (lpParameters && (lpParameters->dwParams & RIL_PARAM_SCP_FILEID)) {

        // Add ",<fileid>" to prefix string
        (void)_snprintfz(szPrefixWalk, MAX_PATH - (szPrefixWalk - szPrefix), ",%u", lpParameters->dwFileID);
        szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szPrefixWalk);

        if ((lpParameters->dwParams & RIL_PARAM_SCP_PARAM1) &&
            (lpParameters->dwParams & RIL_PARAM_SCP_PARAM2) &&
            (lpParameters->dwParams & RIL_PARAM_SCP_PARAM3)) {
            // Add ",<p1>,<p2>,<p3>" to prefix string
            (void)_snprintfz(szPrefixWalk, MAX_PATH - (szPrefixWalk - szPrefix), ",%u,%u,%u",
                             lpParameters->dwParameter1, lpParameters->dwParameter2, lpParameters->dwParameter3);

            // If there's data, add a comma before that data
            if (dwSize)
            {
                strcat(szPrefixWalk, ",");
                cbDataSizeToUse = dwSize;
            }
        }
    }

    // Add "<prefix>\"<data>\"<CR>"
    // NOTE: we take ownership of allocated szCmd
    if (!ComposeCmdWithByteArray(szPrefix, lpbData, cbDataSizeToUse, "\r", szCmd)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, ParseSendRestrictedSimCmd, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    delete szCmd;
#else  // WAVECOM_DRIVER

    HRESULT hr = E_NOTIMPL;
    //Sending +CPINC instead of command of RIL_SIMCMD_STATUS
    if( RIL_SIMCMD_STATUS == dwCommand 
        && ( lpParameters == NULL || (lpParameters->dwParams & RIL_PARAM_SCP_FILEID)== 0 ) )
    {
        CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
        if (!pHandle || !QueueCmd(pHandle, "AT+CPINC\r", CMDOPT_NONE, APIID_SENDRESTRICTEDSIMCMD, ParseQueryPINCounter, NULL, hr)) 
        {
            hr = E_FAIL;
        }
    }
#endif // WAVECOM_DRIVER

    return hr;
}

#if defined(EMP_DRIVER)

class BerTlv
{
public:
    BerTlv() : m_bTag(0), m_uLen(0), m_pbValue(NULL), m_uTotalSize(0) {};
    BerTlv(const BYTE* pRawData, UINT cbSize);

    ~BerTlv() {};

    BYTE GetTag() {return m_bTag;};
    UINT GetLength() {return m_uLen;};
    const BYTE* GetValue() {return m_pbValue;};
    UINT GetTotalSize() {return m_uTotalSize;};

    BOOL BerTlv::Parse(const BYTE* pRawData, UINT cbSize);


private:
    BYTE m_bTag;
    UINT m_uLen;
    const BYTE* m_pbValue;
    UINT m_uTotalSize;
};

BerTlv::BerTlv(const BYTE* pRawData, UINT cbSize)
{
    Parse(pRawData, cbSize);
}

BOOL BerTlv::Parse(const BYTE* pRawData, UINT cbSize)
{
    if (2 > cbSize) {
        // Not enough data for a TLV.
        return FALSE;
    }

    // Tag at index 0.
    BYTE bTag = pRawData[0];
    if (0 == bTag ||
        0xFF == bTag) {
        // Invalid Tag
        return FALSE;
    }

    // Encoded length starts at index 1
    BYTE bValuePos = 0;
    UINT uLen = 0;

    if (0x80 == (0x80 & pRawData[1])) {
        BYTE bLenBytes = 0x7F & pRawData[1];

        if (1 < bLenBytes ||
            3 > cbSize) {
            // Currently only support 1 extra length byte
            return FALSE;
        }

        uLen = pRawData[2];
        bValuePos = 3;
    }
     else {
        uLen = pRawData[1];
        bValuePos = 2;
    }

    // Verify there is enough data available for the value
    if (uLen + bValuePos > cbSize) {
        return FALSE;
    }

    // Verify length and value size are consistent.
    if (cbSize - bValuePos < uLen) {
        // Try and recover using the minimum value.
        uLen = cbSize - bValuePos;
    }

    m_bTag = bTag;
    m_uLen = uLen;
    m_pbValue = pRawData + bValuePos;
    m_uTotalSize = uLen + bValuePos;

    return TRUE;
}


//
//
//
BOOL ParseGetUsimRecordStatus(RILSIMRESPONSE* prsr, RILSIMRECORDSTATUS* prsrs, HRESULT* phr)
{
    BOOL fRet = FALSE;
    const BYTE FCP_TAG = 0x62;
    const BYTE FILE_SIZE_TAG = 0x80;
    const BYTE FILE_DESCRIPTOR_TAG = 0x82;
    const BYTE FILE_ID_TAG = 0x83;
    const MASTER_FILE_ID = 0x3F00;
    DWORD dwRecordType = SIM_RECORDTYPE_UNKNOWN;
    DWORD dwParams = 0;
    DWORD dwItemCount = 0;
    DWORD dwRecordLength = 0;
    DWORD dwSize = 0;
    BerTlv tlvFileDescriptor;
    BerTlv tlvFcp;

    if (NULL == prsr ||
        NULL == prsrs ||
        NULL == phr) {
        goto Exit;
    }

    DWORD cbDataResponseSize = prsr->cbSize - sizeof(RILSIMRESPONSE);

    // Need at least 2 bytes for response data FCP (file control parameters)
    if (2 > cbDataResponseSize) {
        goto Exit;
    }

    // Validate this response is a 3GPP 102 221 SELECT response.
    tlvFcp.Parse(prsr->pbResponse, prsr->cbSize);
    if (FCP_TAG != tlvFcp.GetTag()) {
        goto Exit;
    }

    ASSERT(cbDataResponseSize == tlvFcp.GetTotalSize());
    const BYTE* pbFcpData = tlvFcp.GetValue();
    UINT cbFcpDataSize = tlvFcp.GetLength();

    // Retrieve the File Descriptor data object
    tlvFileDescriptor.Parse(pbFcpData, cbFcpDataSize);
    if (FILE_DESCRIPTOR_TAG != tlvFileDescriptor.GetTag()) {
        goto Exit;
    }

    UINT cbDataUsed = tlvFileDescriptor.GetTotalSize();
    if (cbDataUsed > cbFcpDataSize) {
        goto Exit;
    }

    const BYTE* pbFileDescData = tlvFileDescriptor.GetValue();
    UINT cbFileDescDataSize = tlvFileDescriptor.GetLength();
    // File descriptors should only be 2 or 5 bytes long.
    ASSERT(2 == cbFileDescDataSize || 5 == cbFileDescDataSize);
    if (2 > cbFileDescDataSize) {
        goto Exit;
    }

    BOOL fIsDf = (0x38 == (0x38 & pbFileDescData[0]));
    BYTE bEfStructure = 0x07 & pbFileDescData[0];

    dwParams |= RIL_PARAM_SRS_RECORDTYPE;

    if (fIsDf) {
        dwRecordType = RIL_SIMRECORDTYPE_DEDICATED;
    }

    // or it is an EF or MF.
    else {
        switch (bEfStructure) {
            // Transparent
            case 0x01:
                dwRecordType = RIL_SIMRECORDTYPE_TRANSPARENT;
                break;

            // Linear Fixed
            case 0x02:
                dwRecordType = RIL_SIMRECORDTYPE_LINEAR;
                break;

            // Cyclic
            case 0x06:
                dwRecordType = RIL_SIMRECORDTYPE_CYCLIC;
                break;

           default:
                break;
        }

        if (RIL_SIMRECORDTYPE_LINEAR == dwRecordType ||
            RIL_SIMRECORDTYPE_CYCLIC == dwRecordType) {
            // Need at least 5 bytes
            if (5 > cbFileDescDataSize) {
                goto Exit;
            }

            dwItemCount = pbFileDescData[4];
            dwRecordLength = (pbFileDescData[2] << 4) + (pbFileDescData[3]);

            // Skip checking of consistency with the File Size data object to
            // save time.
            dwSize = dwRecordLength;
            dwParams |= RIL_PARAM_SRS_ITEMCOUNT | RIL_PARAM_SRS_SIZE;
        }
        else if(RIL_SIMRECORDTYPE_TRANSPARENT == dwRecordType) {
            // Retrieve the file size.
            BerTlv tlvCurrent;

            while (cbFcpDataSize > cbDataUsed) {
                if (!tlvCurrent.Parse(
                    pbFcpData + cbDataUsed,
                    cbFcpDataSize - cbDataUsed)) {
                    goto Exit;
                }

                cbDataUsed += tlvCurrent.GetTotalSize();

                if (FILE_SIZE_TAG == tlvCurrent.GetTag()) {
                    if (2 > tlvCurrent.GetLength()) {
                        goto Exit;
                    }

                    const BYTE* pbFileSize = tlvCurrent.GetValue();

                    dwSize = (pbFileSize[0] << 4) + pbFileSize[1];
                    dwParams |= RIL_PARAM_SRS_SIZE;

                    // Size found. Leave loop
                    break;
                }
            }
        }
    }

    // if record type has not been resolved, check for Master file.
    if (RIL_SIMRECORDTYPE_UNKNOWN == dwRecordType) {
        // Next data object should be File ID.
        BerTlv tlvFileId(
            pbFcpData + cbFcpDataSize,
            cbFcpDataSize - cbDataUsed);

        if (FILE_ID_TAG != tlvFileId.GetTag()) {
            goto Exit;
        }

        if (2 != tlvFileId.GetLength()) {
            goto Exit;
        }

        const BYTE* pbFileId = tlvFileId.GetValue();
        UINT uFileId = (pbFileId[0] << 4) + pbFileId[1];

        if (MASTER_FILE_ID != uFileId) {
            goto Exit;
        }

        dwRecordType = RIL_SIMRECORDTYPE_MASTER;
    }

    prsrs->cbSize = sizeof(RILSIMRECORDSTATUS);
    prsrs->dwParams = dwParams;
    prsrs->dwRecordType = dwRecordType;
    prsrs->dwItemCount = dwItemCount;
    prsrs->dwSize = dwSize;

    fRet = TRUE;
Exit:
    return fRet;
}


#endif

//
//
//
HRESULT ParseGetSimRecordStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetSimRecordStatus);
    HRESULT hr = S_OK;
    RILSIMRESPONSE* prsr = NULL;
    RILSIMRECORDSTATUS *prsrs = NULL;
    UINT cbStruct = sizeof(RILSIMRECORDSTATUS);
    DWORD dwFileID;
    DWORD dwTotalSize;

    pBlob = NULL;
    cbBlob = 0;

    // First fill in the RILSIMRESPONSE structure
    void *pBlobTmp=NULL;
    UINT cbBlobTmp=0;
    hr = ParseSendRestrictedSimCmd(szRsp, pBlobTmp, cbBlobTmp);
    prsr = (RILSIMRESPONSE *)pBlobTmp;
    if (FAILED(hr)) {
        goto Error;
    }

    // Now we need to munge this to be a RILSIMRECORDSTATUS structure instead
    prsrs = (RILSIMRECORDSTATUS*)AllocBlob(cbStruct);
    if (!prsrs) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsrs, 0x00, cbStruct);

    // Was there an error of some sort?
    DEBUGCHK((prsr->dwParams & RIL_PARAM_SR_STATUSWORD1) && (prsr->dwParams & RIL_PARAM_SR_STATUSWORD2));
    hr = DetermineSimResponseError(prsr->dwStatusWord1, prsr->dwStatusWord2);
    if (FAILED(hr)) {
        goto Error;
    }

#if defined(EMP_DRIVER)
    if (ParseGetUsimRecordStatus(prsr, prsrs, &hr)) {
        if (SUCCEEDED(hr))
        {
            pBlob = (void*)prsrs;
            cbBlob = cbStruct;
        }

        goto Error;
    }
#endif

    // The response has to be present and at least 7 bytes long
    if ((!(prsr->dwParams & RIL_PARAM_SR_RESPONSE)) || (prsr->cbSize - sizeof(RILSIMRESPONSE) < 7))
    {
        hr = E_FAIL;
        goto Error;
    }

    // The file ID should be in bytes 5 and 6 -- in either case, we need at least 7 bytes
    dwFileID = (prsr->pbResponse[4] << 8) | prsr->pbResponse[5];
    prsrs->cbSize = cbStruct;
    prsrs->dwParams = (RIL_PARAM_SRS_SIZE | RIL_PARAM_SRS_RECORDTYPE);
    prsrs->dwRecordType = RIL_SIMRECORDTYPE_UNKNOWN;
    if (IsElementarySimFile(dwFileID))
    {
        // Need at least 14 bytes for an elementary file
        if (prsr->cbSize - sizeof(RILSIMRESPONSE) < 14)
        {
            hr = E_FAIL;
            goto Error;
        }

        // Byte 14 tells us the file type
        switch (prsr->pbResponse[13])
        {
            case 0x00:
            case 0x02: // This is what OEM1 gives us, but it's not in 11.11!
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_TRANSPARENT;
                break;
            case 0x01:
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_LINEAR;
                break;
            case 0x03:
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_CYCLIC;
                break;
            default:
                break;
        }

        // Set more fields based on the file type
        dwTotalSize = (prsr->pbResponse[2] << 8) | prsr->pbResponse[3];
        if (prsrs->dwRecordType == RIL_SIMRECORDTYPE_TRANSPARENT)
        {
            // Size is bytes 3 and 4
            prsrs->dwParams |= RIL_PARAM_SRS_SIZE;
            prsrs->dwSize = dwTotalSize;
        }
        else if (prsrs->dwRecordType != RIL_SIMRECORDTYPE_UNKNOWN)
        {
            // Make sure we have a fifteenth byte in this case
            if (prsr->cbSize - sizeof(RILSIMRESPONSE) < 15)
            {
                hr = E_FAIL;
                goto Error;
            }

            prsrs->dwParams |= (RIL_PARAM_SRS_SIZE | RIL_PARAM_SRS_ITEMCOUNT);
            prsrs->dwSize = prsr->pbResponse[14];
            prsrs->dwItemCount = dwTotalSize / prsrs->dwSize;
        }
    }
    else
    {
        // We only set the file type, which is determined by byte 7
        switch (prsr->pbResponse[6])
        {
            case 0x01:
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_MASTER;
                break;
            case 0x02:
                prsrs->dwRecordType = RIL_SIMRECORDTYPE_DEDICATED;
                break;
            default:
                break;
        }
    }

    pBlob = (void*)prsrs;
    cbBlob = cbStruct;

Error:
    if (FAILED(hr)) {
        FreeBlob(prsrs);
    }
    FreeBlob(prsr);
    return hr;
}

//
//
//
HRESULT RILDrv_GetSimRecordStatus(DWORD dwParam, DWORD dwFileID)
{
    FUNCTION_TRACE(RILDrv_GetSimRecordStatus);
#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER)
    // HW-SPECIFIC: OEM1 uses a different set of AT commands than implied by 11.11

    DWORD dwParameter1, dwParameter2, dwParameter3;
    DWORD dwRestrictedSIMCmd;
    WORD wFileID = (WORD) (dwFileID & 0xffff);
    LPBYTE lpbData = NULL;
    DWORD dwDataSize = 0;
    LPSTR szCmd = NULL;
    char szPrefix[MAX_PATH];
    LPSTR szPrefixWalk = szPrefix;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    dwParameter1 = 0x00;
    dwParameter2 = 0x00;

#if defined(EMP_DRIVER)
    // Always request the max size to allow for USIM responses, which
    // differ in structure from GSM SIM responses.
    dwParameter3 = 255;
#else
    // Is this an elementary file?
    if (IsElementarySimFile(dwFileID))
    {
        // This is an elementary file -- send GET RESPONSE
        dwParameter3 = 15;  // min required length of response for EF
    }
    else
    {
        // This is not an elementary file
        dwParameter3 = 22;  // min required length of response for DF/MF
    }
#endif

#if defined(OEM1_DRIVER) || defined(EMP_DRIVER)
    dwRestrictedSIMCmd = g_rgdwRestrictedSIMCmds[RIL_SIMCMD_GETRESPONSE];
#else
    dwRestrictedSIMCmd = g_rgdwRestrictedSIMCmds[RIL_SIMCMD_STATUS];
#endif

    // Add "AT+CRSM=<command>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_PATH - (szPrefixWalk - szPrefix), "AT+CRSM=%u", dwRestrictedSIMCmd);
    szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szPrefixWalk);

    // Add ",<fileid>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_PATH - (szPrefixWalk - szPrefix), ",%u", dwFileID);
    szPrefixWalk = strchr(szPrefixWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szPrefixWalk);

    // Add ",<p1>,<p2>,<p3>" to prefix string
    (void)_snprintfz(szPrefixWalk, MAX_PATH - (szPrefixWalk - szPrefix), ",%u,%u,%u",
                     dwParameter1, dwParameter2, dwParameter3);

    // If there's data, add a comma before that data
    if (dwDataSize)
    {
        strcat(szPrefixWalk, ",");
    }

    // Add "<prefix>\"<data>\"<CR>"
    // NOTE: we take ownership of allocated szCmd
    if (!ComposeCmdWithByteArray(szPrefix, lpbData, dwDataSize, "\r", szCmd)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_GETSIMRECORDSTATUS, ParseGetSimRecordStatus, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    delete szCmd;

#elif !defined(WAVECOM_DRIVER) // WAVECOM_DRIVER
    // HW-SPECIFIC: WaveCom doesn't support AT+CSIM
    // Send down a SELECT command to find out information about this file

    BYTE rgbSelect[] = { 0xA4, 0x00, 0x00, 0x02, 0x00, 0x00 };
    DWORD dwSize = sizeof(rgbSelect);
    LPSTR szCmd = NULL;
    char szPrefix[MAX_PATH];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Set the file ID in the last two bytes, please
    rgbSelect[4] = (BYTE) ((dwFileID & 0xff00) >> 8);
    rgbSelect[5] = (BYTE) (dwFileID & 0x00ff);

    // Add "AT+CSIM=<length>," to prefix string
    (void)_snprintfz(szPrefix, MAX_PATH, "AT+CSIM=%u,", dwSize * 2);

    // Add "<prefix>\"<command>\"<CR>"
    // NOTE: we take ownership of allocated szCmd
    if (!ComposeCmdWithByteArray(szPrefix, rgbSelect, dwSize, "\r", szCmd)) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_GETSIMRECORDSTATUS, ParseGetSimRecordStatus, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    delete szCmd;

#else  // OEM1_DRIVER || OEM2_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // OEM1_DRIVER || OEM2_DRIVER

    return hr;
}


//
//
//
static HRESULT ParseGetSimToolkitProfile(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetSimToolkitProfile);
    UINT i;
    UINT nValue;
    UINT cbProfileString;
    LPSTR szProfileString = NULL;
    LPSTR pchProfileStringWalk;
    UINT cbProfile;
    BYTE* pbProfileWalk;
    BYTE* pbProfile = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>%SATC: <mode>,"
    // for EMP the response is "<prefix>*STKC: <n>,<StkPrfl>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
#ifdef EMP_DRIVER
        !MatchStringBeginning(szRsp, "*STKC: ", szRsp) ||
#else
        !MatchStringBeginning(szRsp, "%SATC: ", szRsp) ||
#endif
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<profile>"
    // NOTE: we take ownership of allocated szProfileString
#ifdef EMP_DRIVER
    // <profile> is a quoted string in EMP
    if (!ParseUnlimitedString(szRsp, szProfileString, cbProfileString, szRsp)) {
#else
    if (!ParseUnlimitedUnquotedString(szRsp, '\r', szProfileString, cbProfileString, szRsp)) {
#endif
        hr = E_FAIL;
        goto Error;
    }
    DEBUGCHK(0 == (cbProfileString - 1) % 2);
    cbProfile = (cbProfileString - 1) / 2;

    // Allocate the byte array of needed size
    pbProfile = (BYTE*)AllocBlob(cbProfile);
    if (!pbProfile) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    // Decode the profile
    pbProfileWalk = pbProfile;
    pchProfileStringWalk = szProfileString;
    for (i = 0; i < cbProfile; i++) {
        *pbProfileWalk = SemiByteCharsToByte(*pchProfileStringWalk, *(pchProfileStringWalk + 1));
        pbProfileWalk++;
        pchProfileStringWalk += 2;
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)pbProfile;
    cbBlob = cbProfile;

Error:
    if (FAILED(hr)) {
        FreeBlob(pbProfile);
    }
    delete[] szProfileString;
    return hr;
}


//
//
//
HRESULT RILDrv_GetSimToolkitProfile(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetSimToolkitProfile);
#if defined(RIL_ENABLE_SIMTK) && (defined(OEM1_DRIVER)||defined(WAVECOM_DRIVER))
    // HW-SPECIFIC: This uses a OEM1-specific AT%SATC command or a EMP-specific *STKC command

    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef EMP_DRIVER
    if (!QueueCmd(pHandle, "AT*STKC?\r", CMDOPT_NONE, APIID_GETSIMTOOLKITPROFILE, ParseGetSimToolkitProfile, NULL, hr)) {
#else
    if (!QueueCmd(pHandle, "AT%SATC?\r", CMDOPT_NONE, APIID_GETSIMTOOLKITPROFILE, ParseGetSimToolkitProfile, NULL, hr)) {
#endif
        hr = E_FAIL;
        goto Error;
    }

Error:
#else  // OEM1_DRIVER || EMP_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // OEM1_DRIVER || EMP_DRIVER

    return hr;
}


//
//
//
HRESULT RILDrv_SetSimToolkitProfile(DWORD dwParam, const BYTE* lpbProfile, DWORD dwSize)
{
    FUNCTION_TRACE(RILDrv_SetSimToolkitProfile);
#if (defined(RIL_ENABLE_SIMTK) && (defined(OEM1_DRIVER)||defined(WAVECOM_DRIVER))) || defined(OEM2_SIMTOOLKIT)
    // HW-SPECIFIC: This uses a OEM1-specific AT%SATC command

    DEBUGCHK(NULL != lpbProfile);
    DEBUGCHK(0 != dwSize);

    CHAR szPrefix[MAX_PATH];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpbProfile) {
        hr = E_FAIL;
        goto Error;
    }

    // Store terminal profile
    if (!g_RilSTKTerminalProfile.SetByte((const LPBYTE)lpbProfile, dwSize))
    {
        hr = E_FAIL;
        goto Error;
    }

#if defined(OEM2_SIMTOOLKIT)
    _snprintfz(szPrefix, ARRAY_LENGTH(szPrefix),"AT+STPD=%d,%s\r", g_RilSTKTerminalProfile.GetSize(), g_RilSTKTerminalProfile.GetText());
#elif defined(EMP_DRIVER)
    // EMP uses *STKC to set SIM toolkit profile and the <profile> is a quoted string
    _snprintfz(szPrefix, ARRAY_LENGTH(szPrefix),"AT*STKC=1,\"%s\"\r", g_RilSTKTerminalProfile.GetText());
#else
    _snprintfz(szPrefix, ARRAY_LENGTH(szPrefix),"AT%%SATC=1,%s;+CFUN=1\r", g_RilSTKTerminalProfile.GetText());
#endif

    if (!QueueCmd(pHandle, szPrefix, CMDOPT_NONE, APIID_SETSIMTOOLKITPROFILE, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
#else  // OEM1_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // OEM1_DRIVER

    return hr;
}


//
//
//
static HRESULT ParseSendSimToolkitEnvelopeCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseSendSimToolkitEnvelopeCmd);
    UINT i;
    UINT cbResponseString;
    LPSTR szResponseString = NULL;
    LPSTR pchResponseStringWalk;
    UINT cbResponse;
    BYTE* pbResponseWalk;
    BYTE* pbResponse = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>%SATE: "
    // On EMP platform the format is "<prefix>*STKE"
    if (!ParseRspPrefix(szRsp, szRsp) ||
#ifdef EMP_DRIVER
        !MatchStringBeginning(szRsp, "*STKE: ", szRsp)) {
#else
        !MatchStringBeginning(szRsp, "%SATE: ", szRsp)) {
#endif
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<response>"
    // NOTE: we take ownership of allocated szResponseString
#ifdef EMP_DRIVER
    if (!ParseUnlimitedString(szRsp, szResponseString, cbResponseString, szRsp)) {
#else
    if (!ParseUnlimitedUnquotedString(szRsp, '\r', szResponseString, cbResponseString, szRsp)) {
#endif
        hr = E_FAIL;
        goto Error;
    }
    DEBUGCHK(0 == (cbResponseString - 1) % 2);
    cbResponse = (cbResponseString - 1) / 2;

    // Allocate the byte array of needed size
    pbResponse = (BYTE*)AllocBlob(cbResponse);
    if (!pbResponse) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    // Decode the response
    pbResponseWalk = pbResponse;
    pchResponseStringWalk = szResponseString;
    for (i = 0; i < cbResponse; i++) {
        *pbResponseWalk = SemiByteCharsToByte(*pchResponseStringWalk, *(pchResponseStringWalk + 1));
        pbResponseWalk++;
        pchResponseStringWalk += 2;
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)pbResponse;
    cbBlob = cbResponse;

Error:
    if (FAILED(hr)) {
        FreeBlob(pbResponse);
    }
    delete[] szResponseString;
    return hr;
}


//
//
//
HRESULT RILDrv_SendSimToolkitEnvelopeCmd(DWORD dwParam, const BYTE* lpbCommand, DWORD dwSize)
{
    FUNCTION_TRACE(RILDrv_SendSimToolkitEnvelopeCmd);
#if defined(RIL_ENABLE_SIMTK) && defined(OEM1_DRIVER) && !defined(OEM2_SIMTOOLKIT) || defined(EMP_DRIVER)

    // HW-SPECIFIC: This uses a OEM1-specific AT%SATE command
    DEBUGCHK(NULL != lpbCommand);
    DEBUGCHK(0 != dwSize);

    LPSTR szCmd = NULL;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpbCommand) {
        hr = E_FAIL;
        goto Error;
    }

    // Add "AT%SATE=<command><CR>"
    // NOTE: we take ownership of allocated szCmd
#ifdef EMP_DRIVER
    // EMP uses *STKE to send a command
    // note that ComposeCmdWithByteArray is modified to add quotes to the string
    if (!ComposeCmdWithByteArray("AT*STKE=", lpbCommand, dwSize, "\r", szCmd)) {
#else
    if (!ComposeCmdWithByteArray("AT%SATE=", lpbCommand, dwSize, "\r", szCmd)) {
#endif
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SENDSIMTOOLKITENVELOPECMD, ParseSendSimToolkitEnvelopeCmd,
                  NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    delete szCmd;

#elif defined(OEM2_SIMTOOLKIT)
    // For OEM2, we really aren't sending a Envelope command, since OEM2 does
    // not have such a command. However, the RILDrv_SendSimToolkitEnvelopeCmd
    // call is used exclusively to select a menu item from the main menu.
    // To maintain API compatibility RILDrv_SendSimToolkitEnvelopeCmd will
    // send the OEM2 Menu Selection command +STMS.

    char szCmd[MAX_PATH];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_PATH, "AT+STMS=%s\r", lpbCommand);
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SENDSIMTOOLKITENVELOPECMD, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:

#else  // OEM1_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // OEM1_DRIVER

    return hr;
}


//
//
//
//
HRESULT RILDrv_SendSimToolkitCmdResponse(DWORD dwParam, const RILSIMTOOLKITRSP* pRsp, LPBYTE pDetails, const DWORD dwDetailSize)
{
    FUNCTION_TRACE(RILDrv_SendSimToolkitCmdResponse);

#if  defined(OEM1_DRIVER) || defined(OEM2_SIMTOOLKIT)||defined(WAVECOM_DRIVER)        
    LPSTR szCmd = NULL;
    LPSTR szCmdPrefix = NULL;
    LPSTR szCmdSuffix = NULL;
    LPSTR szResponse = NULL;
    DWORD dwSize = 0;
    DWORD dwCmdSize = 0;
    CRilSimToolkitResponse crstrRsp;
    CRilInstanceHandle* pHandle = NULL;
    HRESULT hr = S_OK;
 

    pHandle = ExtractHandle(dwParam);

    DEBUGCHK(NULL != pRsp);
    DEBUGCHK(NULL != pHandle);

    if (!pHandle || !pRsp) {
        hr = E_FAIL;
        goto Error;
    }

    // Check for needed values in the response parameter. We need to command
    // type to determine the correct AT command to send to the radio.
    if (!(RIL_PARAM_SIMTKIT_RSP_TYPE & pRsp->dwParams)) {
        DEBUGCHK(FALSE);
        hr = E_FAIL;
        goto Error;
    }

    // Create a radio formatted response. The CRilSimToolkitResponse class
    // will automatically parse the command appropriately for specific radio.
    hr = crstrRsp.CreateResponse(pRsp, pDetails, dwDetailSize, &szResponse, &dwSize);

    if (FAILED(hr)) {
        DEBUGCHK(FALSE);
        goto Error;
    }
    else if(0 >= dwSize) {
        // CreateResponse did not create a response for this command,
        // which is acceptable. Don't send anything to the radio
        // if a response was not created.
        hr = E_FAIL;
        goto Error;
    }

    // Setup the AT command prefix and suffix according to what radio we are using.
    // We send out a different command for Setup Menu replies.
#if defined(OEM2_SIMTOOLKIT)
    if (SIM_NOTIFY_SETUPMENU_REPLY == pRsp->dwType) {
        szCmdPrefix = "AT+STMS=";
    }
    else {
        szCmdPrefix = "AT+STCR=";
    }
#elif defined(OEM1_DRIVER)
    // For newer OEM1 stacks the SIM toolkit envelope and response command can have quotes
    // (but are not required) around the data portion of the response. Although, having
    // quotes around the data is a more proper form, we do not add them at this time to
    // remain as compatible as possible with older stacks.
    if (SIM_NOTIFY_SETUPMENU_REPLY == pRsp->dwType) {
        szCmdPrefix = "AT%SATE=";
    }
    else {
        szCmdPrefix = "AT%SATR=";
    }
#else
        szCmdPrefix ="AT+STGR=";                        
#endif // defined(OEM2_SIMTOOLKIT)

    szCmdSuffix = "\r";
    dwCmdSize = 0;

    // Create buffer large enough to contain the entire AT command to be sent to the radio.
    // This is the size of the command prefix, the command suffix, the size of the completely
    // parsed command plus null termination.
    dwCmdSize = strlen(szCmdPrefix) + strlen(szCmdSuffix) + dwSize + 1;
    szCmd = new char[dwCmdSize];
    if (NULL == szCmd) {
        DEBUGCHK(FALSE);
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    if ((SIM_NOTIFY_GETINPUT == pRsp->dwType ||SIM_NOTIFY_GETINKEY==pRsp->dwType) && (SIM_RESPONSE_OK==pRsp->dwResponse))
    {
        // if the command is getinput/getinkey, the at cmd sending by "<cmd type>, <result>" at first,  and the sending the <data>with <CTRL Z>
        if (!QueueMultipartCmd(pHandle, "AT+STGR=3,1\r", szResponse, CMDOPT_RETRYONSIMLOCKED | CMDOPT_INTERMEDIATERESPONSE | CMDOPT_IGNORERADIOOFF, APIID_SENDSIMTOOLKITCMDRESPONSE, NULL, NULL, hr))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {       
        // Create the command string
        (void)_snprintfz(szCmd, dwCmdSize, "%s%s%s", szCmdPrefix, szResponse, szCmdSuffix);

        // Send the command
        if (!QueueCmd(pHandle, szCmd, CMDOPT_IGNORERADIOOFF, APIID_SENDSIMTOOLKITCMDRESPONSE, NULL, NULL, hr))
        {
            hr = E_FAIL;
            goto Error;
        }
    }


Error:

    if(szResponse)
    {
        delete[] szResponse;
        szResponse = NULL;
    }
    if(szCmd)
    {
        delete[] szCmd;
        szCmd = NULL;
    }

    return hr;

#else

    HRESULT hr = E_NOTIMPL;
    return hr;

#endif // OEM1_DRIVER
}


//
//
//
HRESULT RILDrv_TerminateSimToolkitSession(DWORD dwParam, DWORD dwCause)
{
    FUNCTION_TRACE(RILDrv_TerminateSimToolkitSession);
#if defined(RIL_ENABLE_SIMTK) && defined(OEM1_DRIVER)
    // HW-SPECIFIC: This uses a OEM1-specific AT%SATT command

    DEBUGCHK(0 != dwCause);
    DEBUGCHK(NUM_SIMTOOLKITTERMCAUSES > dwCause);

    HRESULT hr = S_OK;
    char szCmd[MAX_PATH];
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_PATH, "AT%%SATT=%u\r", g_rgdwSIMToolkitTermCauses[dwCause]);
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_TERMINATESIMTOOLKITSESSION, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
#else  // OEM1_DRIVER

    HRESULT hr = E_NOTIMPL;
#endif // OEM1_DRIVER

    return hr;
}


//
//
//
//
HRESULT RILDrv_SendSimToolkitEventDownload(DWORD dwParam, const DWORD dwEvent, LPBYTE pData, const DWORD dwDataSize)
{
    CRilInstanceHandle* pHandle;
    CRilSimToolkitResponse crstrRsp;
    LPSTR szResponse = NULL;
    DWORD dwSize = 0;
    LPSTR szCmd = NULL;
    LPSTR szCmdPrefix = NULL;
    LPSTR szCmdSuffix = NULL;
    DWORD dwCmdSize = 0;
    HRESULT hr = S_OK;
    FUNCTION_TRACE(RILDrv_SendSimToolkitEventDownload);


    pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Exit;
    }

    // Create a radio formatted Event Download response. The CRilSimToolkitResponse class
    // will automatically parse the command appropriately for specific radio.
    hr = crstrRsp.CreateEventDownload(dwEvent, pData, dwDataSize, &szResponse, &dwSize);

    // Setup the AT command prefix and suffix according to what radio we are using.
#if defined(OEM2_SIMTOOLKIT)
    szCmdPrefix = "AT+STEV=";
#elif defined(OEM1_DRIVER)
    szCmdPrefix = "AT%SATE=";
#elif defined(EMP_DRIVER)
    szCmdPrefix = "AT*STKE=";
#endif // defined(OEM2_SIMTOOLKIT)

    szCmdSuffix = "\r";
    dwCmdSize = 0;

    // Create buffer large enough to contain the entire AT command to be sent to the radio.
    // This is the size of the command prefix, the command suffix, the size of the completely
    // parsed command plus null termination.
    dwCmdSize = strlen(szCmdPrefix) + strlen(szCmdSuffix) + dwSize + 1;
    szCmd = new char[dwCmdSize];
    if (NULL == szCmd) {
        DEBUGCHK(FALSE);
        hr = E_OUTOFMEMORY;
        goto Exit;
    }

    // Create the command string
    (void)_snprintfz(szCmd, dwCmdSize, "%s%s%s", szCmdPrefix, szResponse, szCmdSuffix);

    // Send the command
    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SENDSIMTOOLKITEVENTDOWNLOAD, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Exit;
    }

Exit:
    delete[] szResponse;
    delete[] szCmd;

    return hr;
}


//
//
//
static HRESULT ParseGetCostInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetCostInfo);
    UINT nValue;
    RILCOSTINFO* prci = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prci = (RILCOSTINFO*)AllocBlob(sizeof(RILCOSTINFO));
    if (!prci) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prci, 0x00, sizeof(RILCOSTINFO));
    prci->cbSize = sizeof(RILCOSTINFO);

    // Parse "<prefix>+CAOC: \"<ccm>\"<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CAOC: \"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nValue, szRsp)        ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
#ifndef EMP_DRIVER
        // +CAOC response may not exist on EMP platform
        hr = E_FAIL;
        goto Error;
#endif
    } else {
        prci->dwCCM = nValue;
        prci->dwParams |= RIL_PARAM_CSTI_CCM;
    }

    // Parse "<prefix>+CACM: \"<acm>\"<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CACM: \"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nValue, szRsp)        ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwACM = nValue;
    prci->dwParams |= RIL_PARAM_CSTI_ACM;

    // Parse "<prefix>+CAMM: \"<acm_max>\"<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                    ||
        !MatchStringBeginning(szRsp, "+CAMM: \"", szRsp) ||
        !ParseHexUInt(szRsp, TRUE, nValue, szRsp)        ||
        !MatchStringBeginning(szRsp, "\"", szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwMaxACM = nValue;
    prci->dwParams |= RIL_PARAM_CSTI_MAXACM;

    // Parse "<prefix>+CPUC: <currency>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CPUC: ", szRsp) ||
        !ParseQuotedEncodedString(ENCODING_TECHARSET, szRsp, prci->wszCurrency, prci->wszCurrency + MAXLENGTH_CURRENCY)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwParams |= RIL_PARAM_CSTI_CURRENCY;

    // Parse "\"<ppu>\"<postfix>"
    if (!MatchStringBeginning(szRsp, ",\"", szRsp)      ||
        !ParseFixedPoint(szRsp, prci->dwCostPerUnit, szRsp) ||
        !MatchStringBeginning(szRsp, "\"", szRsp)       ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
    prci->dwParams |= RIL_PARAM_CSTI_COSTPERUNIT;

    pBlob = (void*)prci;
    cbBlob = sizeof(RILCOSTINFO);

Error:
    if (FAILED(hr)) {
        FreeBlob(prci);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetCostInfo(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetCostInfo);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(pHandle, "AT+CAOC=0;+CACM?;+CAMM?;+CPUC?\r", CMDOPT_NONE, APIID_GETCOSTINFO, ParseGetCostInfo, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_SetCostInfo(DWORD dwParam, const RILCOSTINFO* lpCostInfo, LPCSTR lpszPassword)
{
    FUNCTION_TRACE(RILDrv_SetCostInfo);
    DEBUGCHK(NULL != lpCostInfo);

    char szCmd[MISC_CMDBUF_LENGTH_COST];
    LPSTR szWalk = szCmd;
    char szPrice[MAX_PATH];
    LPSTR szPriceWalk = szPrice;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpCostInfo) {
        hr = E_FAIL;
        goto Error;
    }

    if (!(lpCostInfo->dwParams & RIL_PARAM_CSTI_ACM) && !(lpCostInfo->dwParams & RIL_PARAM_CSTI_MAXACM) &&
        (!(lpCostInfo->dwParams & RIL_PARAM_CSTI_COSTPERUNIT) ||
         !(lpCostInfo->dwParams & RIL_PARAM_CSTI_CURRENCY))) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Add "AT"
    (void)strncpyz(szWalk, "AT", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (lpCostInfo->dwParams & RIL_PARAM_CSTI_ACM) {
        // Add "+CAOC="
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "+CACM=");
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        // Add "<password>, if supplied
        if (lpszPassword) {
            (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }
    }

    if (lpCostInfo->dwParams & RIL_PARAM_CSTI_MAXACM) {
        // Add ";", if this is not the first command
        if (szWalk != szCmd + 2) {  // NO_TYPO: 30
            (void)strncpyz(szWalk, ";", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));
            szWalk++;
        }

        // Add "+CAMM=<acm_max>"
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "+CAMM=%u", lpCostInfo->dwMaxACM);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        // Add ",<password>, if supplied
        if (lpszPassword) {
            (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), ",\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }
    }

    if ((lpCostInfo->dwParams & RIL_PARAM_CSTI_COSTPERUNIT) &&
        (lpCostInfo->dwParams & RIL_PARAM_CSTI_CURRENCY)) {
        // Add ";", if this is not the first command
        if (szWalk != szCmd + 2) {  // NO_TYPO: 30
            (void)strncpyz(szWalk, ";", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));
            szWalk++;
        }

#if 0 // Not necessary with fixed point values
        LPSTR szPriceDigits;
        int nDecimalPlace;
        int nSign;
        if (0 > lpCostInfo->dbCostPerUnit) {
            // Price can't be negative
            hr = E_INVALIDARG;
            goto Error;
        }

        // Convert the price from double to a string fo digits
        szPriceDigits = _ecvt(lpCostInfo->dbCostPerUnit, MAX_PATH - 2, &nDecimalPlace, &nSign);
        DEBUGCHK(0 != nSign);

        // Generate the string representation of the price value
        if (0 >= nDecimalPlace) {
            *szPriceWalk++ = '0';
            *szPriceWalk++ = '.';

            strncpyz(szPriceWalk, szPriceDigits, MAX_PATH - (szPriceWalk - szPrice));
        } else {
            strncpyz(szPriceWalk, szPriceDigits, nDecimalPlace);
            szPriceWalk = strchr(szPriceWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szPriceWalk);

            *szPriceWalk++ = '.';
            strncpyz(szPriceWalk, szPriceDigits, MAX_PATH - (szPriceWalk - szPrice));
        }
#endif

        // Add "+CPUC=<currency>,<ppu>"
        (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), "+CPUC=\"%s\",\"%u.%.4u\"",
                         USAsciiString(lpCostInfo->wszCurrency),
                         (lpCostInfo->dwCostPerUnit>>16),((lpCostInfo->dwCostPerUnit & 0xFFFF) * 10000)>>16);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        // Add ",<password>, if supplied
        if (lpszPassword) {
            (void)_snprintfz(szWalk, MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd), ",\"%s\"", lpszPassword);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }
    }
    (void)strncpyz(szWalk, "\r", MISC_CMDBUF_LENGTH_COST - (szWalk - szCmd));  // NO_TYPO: 30

    if (!QueueCmd(pHandle, szCmd, CMDOPT_NONE, APIID_SETCOSTINFO, NULL, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


//
//
//
#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH
char gszGetSignalQualityLast[128];
#endif // SUPPRESS_ONLINE_SIGNAL_STRENGTH

BOOL ParseSignalQualityData(LPCSTR szRsp, RILSIGNALQUALITY* prsq, LPCSTR &rszPointer)
{
    BOOL fSuccess = FALSE;
    UINT nRSSI;
    UINT nBER;
#ifdef OEM1_CSQ_NOTIFICATION
    UINT nLvl;
#endif

    rszPointer = szRsp;

    if (!ParseUIntAndVerifyAbove(szRsp, TRUE, 100, nRSSI, szRsp) ||
       !MatchStringBeginning(szRsp, ",", szRsp)                 ||
        !ParseUInt(szRsp, TRUE, nBER, szRsp))
    {
        goto Error;
    }
 #ifdef OEM1_CSQ_NOTIFICATION  // may be CSQ Notfication
    if(MatchStringBeginning(szRsp, ",", szRsp))
    {
        if (!ParseUInt(szRsp, TRUE, nLvl, szRsp))
        {
             goto Error;
        }
    }
#endif  //OEM1_CSQ_NOTIFICATION

    // Calculate signal strength
    prsq->nMinSignalStrength = -113;
    prsq->nMaxSignalStrength = -51;

    prsq->nLowSignalStrength = -110;
    prsq->nHighSignalStrength = -60;

    if (99 == nRSSI) {
        prsq->nSignalStrength = RIL_SIGNALSTRENGTH_UNKNOWN;
    } else {
        DEBUGCHK(32 > nRSSI);
        prsq->nSignalStrength = (2 * nRSSI) + prsq->nMinSignalStrength;
    }
    prsq->dwParams |= (RIL_PARAM_SQ_SIGNALSTRENGTH | RIL_PARAM_SQ_MINSIGNALSTRENGTH | RIL_PARAM_SQ_MAXSIGNALSTRENGTH | RIL_PARAM_SQ_LOWSIGNALSTRENGTH | RIL_PARAM_SQ_HIGHSIGNALSTRENGTH);

#ifdef RIL_WATSON_REPORT
    // Update info cache with last signal strength
    g_RilInfoCache.nSignalStrength = prsq->nSignalStrength;
#endif //RIL_WATSON_REPORT

    // Set bit error rate
    if (NUM_BITERRORRATES > nBER) {
        prsq->dwBitErrorRate = g_rgdwBitErrorRates[nBER];
    } else {
        prsq->dwBitErrorRate = RIL_BITERRORRATE_UNKNOWN;
    }
    prsq->dwParams |= RIL_PARAM_SQ_BITERRORRATE;

    fSuccess = TRUE;
    rszPointer = szRsp;
Error:
    return fSuccess;
}

#ifdef EMP_DRIVER
HRESULT ParseGetSignalQuality(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetSignalQuality);
    RILSIGNALQUALITY* prsq = NULL;
    UINT nBattery, nSignal;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH
    LPCSTR szOrigRsp=szRsp;
#endif // SUPPRESS_ONLINE_SIGNAL_STRENGTH

    prsq = (RILSIGNALQUALITY*)AllocBlob(sizeof(RILSIGNALQUALITY));
    if (!prsq) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsq, 0x00, sizeof(RILSIGNALQUALITY));
    prsq->cbSize = sizeof(RILSIGNALQUALITY);

    // Parse "<prefix>+CIND: <nBattery>,<nSignal>, ... <postfix>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CIND: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nBattery, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp) ||
        !ParseUInt(szRsp, TRUE, nSignal, szRsp) ||
        !FindRspPostfix(szRsp, szRsp)) // Skip other parameters
    {
        hr = E_FAIL;
        goto Error;
    }

    // Calculate signal strength
    prsq->nMinSignalStrength = -113;
    prsq->nMaxSignalStrength = -51;
    prsq->nLowSignalStrength = -110;
    prsq->nHighSignalStrength = -60;

    if (0 == nSignal) {
        prsq->nSignalStrength = RIL_SIGNALSTRENGTH_UNKNOWN;
    } else {
        DEBUGCHK(5 >= nSignal);
        const int nSignalStrengthArray[] = {-113, -98, -82, -66, -51};
        prsq->nSignalStrength = nSignalStrengthArray[nSignal-1];
    }
    prsq->dwParams |= (RIL_PARAM_SQ_SIGNALSTRENGTH | RIL_PARAM_SQ_MINSIGNALSTRENGTH | RIL_PARAM_SQ_MAXSIGNALSTRENGTH | RIL_PARAM_SQ_LOWSIGNALSTRENGTH | RIL_PARAM_SQ_HIGHSIGNALSTRENGTH);

#ifdef RIL_WATSON_REPORT
    // Update info cache with last signal strength
    g_RilInfoCache.nSignalStrength = g_rsq.nSignalStrength;
#endif

    pBlob = (void*)prsq;
    cbBlob = sizeof(RILSIGNALQUALITY);

#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH
    strncpyz(gszGetSignalQualityLast,szOrigRsp,sizeof(gszGetSignalQualityLast));
#endif // SUPPRESS_ONLINE_SIGNAL_STRENGTH

Error:
    if (FAILED(hr)) {
        FreeBlob(prsq);
    }
    return hr;
}

#else

HRESULT ParseGetSignalQuality(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetSignalQuality);
    RILSIGNALQUALITY* prsq = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH
    LPCSTR szOrigRsp=szRsp;
#endif // SUPPRESS_ONLINE_SIGNAL_STRENGTH

    prsq = (RILSIGNALQUALITY*)AllocBlob(sizeof(RILSIGNALQUALITY));
    if (!prsq) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prsq, 0x00, sizeof(RILSIGNALQUALITY));
    prsq->cbSize = sizeof(RILSIGNALQUALITY);

    // Parse "<prefix>+CSQ: <rssi>,<ber><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                            ||
        !MatchStringBeginning(szRsp, "+CSQ: ", szRsp)   ||
        !ParseSignalQualityData(szRsp, prsq, szRsp) ||
        !FindRspPostfix(szRsp, szRsp))          // use FindRsp rather than ParseRsp to be a bit more forgiving...
    {
        hr = E_FAIL;
        goto Error;
    }
#ifdef WAVECOM_DRIVER
    memcpy(&g_rsq , prsq, sizeof(RILSIGNALQUALITY));
    g_fSignalQualityReceived = TRUE;
#endif
    pBlob = (void*)prsq;
    cbBlob = sizeof(RILSIGNALQUALITY);

#ifdef SUPPRESS_ONLINE_SIGNAL_STRENGTH
    strncpyz(gszGetSignalQualityLast,szOrigRsp,sizeof(gszGetSignalQualityLast));
#endif // SUPPRESS_ONLINE_SIGNAL_STRENGTH

Error:
    if (FAILED(hr)) {
        FreeBlob(prsq);
    }
    return hr;
}
#endif

//
//
//
HRESULT RILDrv_GetSignalQuality(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetSignalQuality);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef EMP_DRIVER
    if (!QueueCmd(pHandle, "AT+CIND?\r", CMDOPT_POLLING, APIID_GETSIGNALQUALITY, ParseGetSignalQuality, NULL, hr)) {
#else
    if (!QueueCmd(pHandle, "AT+CSQ\r", CMDOPT_POLLING, APIID_GETSIGNALQUALITY, ParseGetSignalQuality, NULL, hr)) {
#endif
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}


#ifdef OEM1_DRIVER_PM
//
//
//
HRESULT RILDrv_EnablePowerSavings(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_EnablePowerSavings);
    HRESULT hr = S_OK;

    // Check if the radio is on
    if(g_bRadioOff)
        goto Error;

    // Disable the signal quality notifications
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT%CSQ=0\r", CMDOPT_NONE, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) {
        hr = E_FAIL;
        goto Error;
    }
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CREG=0\r", CMDOPT_NONE, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) {
        hr = E_FAIL;
        goto Error;
    }
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CGREG=0\r", CMDOPT_NONE, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_DisablePowerSavings(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_DisablePowerSavings);
    HRESULT hr = S_OK;

    // Check if the radio is on
    if(g_bRadioOff)
        goto Error;

    // Re-enable the signal quality notifications
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT%CSQ=1\r", CMDOPT_NONE, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) {
        hr = E_FAIL;
        goto Error;
    }
#ifndef RIL_NO_CREG_LOCATION_UPDATES
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CREG=2\r", CMDOPT_NONE, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) {
#else
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CREG=1\r", CMDOPT_NONE, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) {
#endif
        hr = E_FAIL;
        goto Error;
    }
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CGREG=1\r", CMDOPT_NONE, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

#elif defined(WAVECOM_DRIVER)

HRESULT ParseEnablePowerSavings(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, PVOID& pfnParseData)
{
    if( pfnParseData != NULL )
    {
        SetEvent((HANDLE)pfnParseData);
    }
    return S_OK;
}
HRESULT RILDrv_EnablePowerSavings(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_EnablePowerSavings);
    HRESULT hr = S_OK;

    // Disable the unsolicited indications
    static HANDLE hResEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    
    if ( NULL == hResEvent 
         || !ResetEvent(hResEvent)
         || !QueueCmdIgnoreRspWithData(APIID_NONE, "AT+TCFG=\"IND\",0\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0, ParseEnablePowerSavings, hResEvent)
         /*|| WAIT_OBJECT_0 != WaitForSingleObject(hResEvent, g_TimeoutAPIDefault)*/ ) 
    {
        hr = E_FAIL;
        goto Error;
    }
    WaitForSingleObject(hResEvent, 1000);
    RETAILMSG(MSG_ON, (TEXT("Disable the unsolicited indications, Done!!\r\n")));
Error:

    return hr;
}

HRESULT RILDrv_DisablePowerSavings(DWORD dwParam)
{
    TBD_FUNCTION(RILDrv_DisablePowerSavings);
    HRESULT hr = S_OK;
    //Get current network state
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CSQ\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, ParseGetSignalQuality, NULL, 0, 0, 0)) {
        hr = E_FAIL;
        goto Error;
    }
    
#ifndef RIL_NO_CREG_LOCATION_UPDATES
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CREG=2\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) {
#else
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CREG=1\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) {
#endif
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CGREG=1\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) 
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+TCFG=\"IND\",255\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, NULL, NULL, 0, 0, 0)) 
    {
        hr = E_FAIL;
        goto Error;
    }
    //Check battery as disable Power saving
    hr = RILDrv_UpdateBatteryStatus();
    if(FAILED(hr))
    {
        RETAILMSG(MSG_ON,(TEXT("RILDrv_UpdateBatteryStatus return ERROR!!!\r\n")));  
    }
Error:

    return hr;
}

static HRESULT ParseUpdateBatteryStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
      RILNOTIFYDATA*  nData = NULL;
      batteryrsp* batRsp = NULL;
      UINT bcs = 0,bcl = 0;
      HRESULT hr = S_OK;

       pBlob = NULL;
       cbBlob = 0;
       nData = (RILNOTIFYDATA*)AllocBlob(sizeof(RILNOTIFYDATA));
       if (!nData)
       {
           hr = E_OUTOFMEMORY;
           goto Error;
       }
       memset(nData, 0x00, sizeof(RILNOTIFYDATA));
       batRsp = (batteryrsp*)&(nData->bRsp);
       // Parse "<prefix>+CBC: <bcs>,<bcl><postfix>"
       if (!ParseRspPrefix(szRsp, szRsp) ||
            !MatchStringBeginning(szRsp, "+CBC: ", szRsp) ||
            !ParseUInt(szRsp, TRUE, bcs, szRsp)        ||
            !MatchStringBeginning(szRsp, ",", szRsp)||
            !ParseUInt(szRsp, TRUE, bcl, szRsp)        ||
            !ParseRspPostfix(szRsp, szRsp))
       {
            RETAILMSG(MSG_ON,(TEXT("!!!!!!!!!!!!!!!!!!!!!!!!!! \r\n")));  
            hr = E_FAIL;
            goto Error;
       }
       nData->pbData = bcs ;
       batRsp->bcs = bcs;
       batRsp->bcl = bcl;
       nData->cbSize= sizeof(RILNOTIFYDATA);
       pBlob = (void*)nData;
       cbBlob = sizeof(RILNOTIFYDATA);
          
Error:
       if(FAILED(hr)&&nData)
       {
            FreeBlob(nData);
            nData = NULL;
       }
       return hr;
}

HRESULT RILDrv_UpdateBatteryStatus()
{
     RETAILMSG(MSG_ON, (TEXT( "RILDrv_UpdateBatteryStatus \r\n")));
     FUNCTION_TRACE(RILDrv_UpdateBatteryStatus);
     HRESULT hr = S_OK;
     CNotificationData* pnd = NULL;
     pnd = new CNotificationData;
     if(pnd == NULL)
     {
         hr = E_FAIL;
         goto Error;
     }
     pnd->DelayInitFromRspBlob(RIL_NOTIFY_CHARGEDETEC);
     if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CBC\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, ParseUpdateBatteryStatus, pnd, 0,0,0)) 
     {
         hr = E_FAIL;
         goto Error;
      }
        
Error:
     if(FAILED(hr)&&pnd)
     {
         delete pnd;
         pnd = NULL;
     }
     return hr;
}

#endif

//
//
//
static HRESULT ParseGetCellTowerInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetCellTowerInfo);
    RILCELLTOWERINFO* prcti = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    prcti = (RILCELLTOWERINFO*)AllocBlob(sizeof(RILCELLTOWERINFO));
    if (!prcti) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prcti, 0x00, sizeof(RILCELLTOWERINFO));
    prcti->cbSize = sizeof(RILCELLTOWERINFO);

#ifdef WAVECOM_DRIVER
    // the AT+CCED command is supported only by Wavecom
    UINT nValue;
    // Parse "<prefix>+CCED: "
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CCED: ", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<mcc>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwMobileCountryCode = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_MOBILECOUNTRYCODE;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<mnc>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwMobileNetworkCode = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_MOBILENETWORKCODE;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<lac>"
    if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwLocationAreaCode = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_LOCATIONAREACODE;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<ci>"
    if (ParseHexUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwCellID = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_CELLID;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<bsic>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwBaseStationID = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_BASESTATIONID;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<bcch>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwBroadcastControlChannel = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_BROADCASTCONTROLCHANNEL;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_lev>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxLevel = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXLEVEL;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_lev_full>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxLevelFull = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXLEVELFULL;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_lev_sub>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxLevelSub = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXLEVELSUB;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_qual>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxQuality = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXQUALITY;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_qual_full>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxQualityFull = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXQUALITYFULL;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<rx_qual_sub>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwRxQualitySub = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_RXQUALITYSUB;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<idle_ts>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwIdleTimeSlot = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_IDLETIMESLOT;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<timing_advance>"
    if (ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        prcti->dwTimingAdvance = nValue;
        prcti->dwParams |= RIL_PARAM_CTI_TIMINGADVANCE;
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
#else
    // this isn't a WAVECOM driver, so use cached info available
    // if radios add functionality to retrieve this info in the future,
    // we can gather it here
    if (FALSE == g_fInitialLocationInfoReceived)
    {
        hr = E_FAIL;
        goto Error;
    }
    else
    {
        // get cached lac, ci
        prcti->dwLocationAreaCode = g_rliLocationInfo.dwLocationAreaCode;
        prcti->dwParams |= RIL_PARAM_CTI_LOCATIONAREACODE;
        prcti->dwCellID = g_rliLocationInfo.dwCellID;
        prcti->dwParams |= RIL_PARAM_CTI_CELLID;
    }

#endif

    pBlob = (void*)prcti;
    cbBlob = sizeof(RILCELLTOWERINFO);

Error:
    if (FAILED(hr)) {
        FreeBlob(prcti);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetCellTowerInfo(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetCellTowerInfo);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

#ifdef WAVECOM_DRIVER
    // HW-SPECIFIC: This uses a WaveCom-specific AT+CCED command
    if (!QueueCmd(pHandle, "AT+CCED=0,5\r", CMDOPT_NONE, APIID_GETCELLTOWERINFO, ParseGetCellTowerInfo,
                  NULL, hr))
#else  // WAVECOM_DRIVER
    // No AT command available for OEM1 and OEM2, get cached data
    if (!QueueCmd(pHandle, NULL, CMDOPT_NOOP, APIID_GETCELLTOWERINFO, ParseGetCellTowerInfo,
                  NULL, hr))
#endif // WAVECOM_DRIVER
    {
        hr = E_FAIL;
    }

Error:
    return hr;
}

#ifdef OEM1_DRIVER
static HRESULT ParseBuildInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseBuildInfo);

    HRESULT hr = S_OK;
    CHAR * szBuildInfo = NULL;

    pBlob = NULL;
    cbBlob = 0;

    szBuildInfo = (CHAR*)AllocBlob(MAX_PATH);

    if (!ParseUnquotedString(szRsp, '\r', szBuildInfo, MAX_PATH, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = szBuildInfo;
    cbBlob = MAX_PATH;

Error:
    if (FAILED(hr)) {
        FreeBlob(szBuildInfo);
    }
    return hr;
    return S_OK;
}
#endif



#ifdef SUPPRESS_ONLINE_BATTERY_INFO
char gszGetBatteryInfoLast[128];
#endif // SUPPRESS_ONLINE_BATTERY_INFO

HRESULT ParseBatteryInfo(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
#define VOLTAGE_RESP_FIELD  "ADC 1 ="

    char*   pch;
    DWORD*  pdwVoltage;

    FUNCTION_TRACE(ParseBatteryInfo);

#ifdef SUPPRESS_ONLINE_BATTERY_INFO
    LPCSTR szOrigRsp=szRsp;
#endif // SUPPRESS_ONLINE_BATTERY_INFO

    pBlob = NULL;
    cbBlob = 0;

    pdwVoltage = (DWORD *)AllocBlob(sizeof(DWORD));
    if( NULL == pdwVoltage)
    {
        ASSERT( FALSE );
        goto Error;
    }

    // search for ADC 1
    pch = strstr( szRsp, VOLTAGE_RESP_FIELD );
    if( NULL == pch )
    {
        ASSERT( FALSE );
        goto Error;
    }

    pch += strlen( VOLTAGE_RESP_FIELD );

    while( *pch == ' ' ) // remove leading spaces
    {
        pch++;
    }

    sscanf( pch, "%x", pdwVoltage );

    pBlob = pdwVoltage;
    cbBlob = sizeof(DWORD);

#ifdef SUPPRESS_ONLINE_BATTERY_INFO
    strncpyz(gszGetBatteryInfoLast,szOrigRsp,sizeof(gszGetBatteryInfoLast));
#endif // SUPPRESS_ONLINE_BATTERY_INFO

    return S_OK;

Error:
    FreeBlob(pdwVoltage);
    return E_FAIL;
}


//
/*===========================================================================
	FUNCTION: RILDrv_CheckLed  

	DESCRIPTION:
		Call the  function to send AT COMMAND to QCT6280  in order to check LED 

	PROTOTYPE:
		HRESULT RILDrv_CheckLed(DWORD dwParam,LPDETAILEDRILDEVSPECIFIC devspsec)

	PARAMETERS:
	       dwParam:[IN]  
		devspsec:[IN]  pointer to DETAILEDRILDEVSPECIFIC entery.
		
		typedef struct detailedrildevspecific_tag {
		    DWORD cbSize;                       // @field structure size in bytes
		    DWORD dwDevSpecificCommand;         // one of the RIL_DEVSPECIFICPARAM_ that require data
		    PVOID lpData;                      // pointer to data specific to dwDevSpecificCommand
		} DETAILEDRILDEVSPECIFIC, *LPDETAILEDRILDEVSPECIFIC;
		
	RETURN VALUE
		none
===========================================================================*/
  /*the implement of led was devided two steps:
     first:
        AT+TLED=<LEDxC>,<period>,<pattern>,<CHG_SET>,<ACT_SET>");
        these paremeters control the led model,which is similar with the document of DF module.
     second:
        AT+LED=<index>,<mode>

        index:the index of LED (1~3);
        mode:LED worked by this mode;
        0:always On;
        1:Use LED1( follow first step setting(LEDxC=1)) control LED;
        2,Use LED2( follow first step setting(LEDxC=2)) control LED;
        3~6:reserved;
        7:close LED;
        after the first step setting ,the second step will set which led for working.
-*/

HRESULT RILDrv_CheckLed(DWORD dwParam,LPDETAILEDRILDEVSPECIFIC  devspsec)
{
    RETAILMSG(MSG_ON, (TEXT( "RILDrv_CheckLed \r\n")));
    FUNCTION_TRACE(RILDrv_CheckLed);
    char szCmd[MAX_PATH] = {0};
    LPSTR szWalk = szCmd;
    HRESULT hr = E_FAIL;
    BOOL  IsOff = FALSE;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if(!pHandle||devspsec == NULL ||( LEDCHECKINFO *)devspsec->lpData == NULL)
    {
        goto Error;
    }
    LEDCHECKINFO * p = ( LEDCHECKINFO *)devspsec->lpData;
    switch(p->OffOnBlink)
    {
        case 0:/*Off*/
            IsOff = TRUE;
            (void)strncpyz(szWalk, "AT+TLED=1,0,7,1,1;", MAX_PATH- (szWalk - szCmd));
            break;
        case 1:/*On*/
            (void)strncpyz(szWalk, "AT+TLED=1,0,7,1,1;", MAX_PATH- (szWalk - szCmd));
            break;
        case 2:/*slow flash*/
            (void)strncpyz(szWalk, "AT+TLED=1,4,2,1,1;", MAX_PATH- (szWalk - szCmd));
            break;
        case 3:/*fast flash*/
            (void)strncpyz(szWalk, "AT+TLED=1,1,2,1,1;", MAX_PATH- (szWalk - szCmd));
            break;
        default:
            RETAILMSG(MSG_ON, (L"[RILDrv_CheckLed] p->OffOnBlink =%d\r\n",p->OffOnBlink));
            goto Error;
    }
    szWalk = strchr(szWalk, '\0');
    if(IsOff ==FALSE)
    {
        switch(p->Ledindex)
        {
            /*red Led*/
            case 1:                                /*Blue*/   /*Green*/   /*Red*/
                (void)strncpyz(szWalk,  "+TLED=1,7;+TLED=2,7;+TLED=3,1\r", MAX_PATH-(szWalk-szCmd));         
                break;
            /*Green Led*/
            case 2:                                /*Blue*/   /*Green*/   /*Red*/
                (void)strncpyz(szWalk,  "+TLED=1,7;+TLED=2,1;+TLED=3,7\r", MAX_PATH-(szWalk-szCmd)); 
                break;
            /*Blue Led*/
            case 3:                                /*Blue*/   /*Green*/   /*Red*/
                (void)strncpyz(szWalk,  "+TLED=1,1;+TLED=2,7;+TLED=3,7\r", MAX_PATH-(szWalk-szCmd)); 
                break;
            /*Yellow Led*/
            case 4:                              /*Blue*/   /*Green*/   /*Red*/
                (void)strncpyz(szWalk,  "+TLED=1,7;+TLED=2,1;+TLED=3,1\r", MAX_PATH-(szWalk-szCmd)); 
                break;
            /*Pink Led*/
            case 5:                              /*Blue*/   /*Green*/   /*Red*/
                (void)strncpyz(szWalk,  "+TLED=1,1;+TLED=2,7;+TLED=3,1\r", MAX_PATH-(szWalk-szCmd));         
                break;
            /*Cerulean Led*/
            case 6:                               /*Blue*/   /*Green*/   /*Red*/
                (void)strncpyz(szWalk,  "+TLED=1,1;+TLED=2,1;+TLED=3,7\r", MAX_PATH-(szWalk-szCmd));
                break;
            /*White Led*/
            case 7:                              /*Blue*/   /*Green*/   /*Red*/
                (void)strncpyz(szWalk,  "+TLED=1,1;+TLED=2,1;+TLED=3,1\r", MAX_PATH-(szWalk-szCmd));
                break;
            default:
                RETAILMSG(MSG_ON, (L"[RILDrv_CheckLed] p->OffOnBlink =%d\r\n",p->OffOnBlink));
                goto Error;
        }
   }
   else
   {
       (void)strncpyz(szWalk,  "+TLED=1,7;+TLED=2,7;+TLED=3,7\r", MAX_PATH-(szWalk-szCmd));        
   }
   if (!QueueCmd(pHandle, szCmd, CMDOPT_IGNORERADIOOFF, APIID_DEVSPECIFIC, NULL, NULL, hr)) 
   {
       hr = E_FAIL;
       goto Error;
   }
   
Error:
    return hr;
}

#define RIL_HANDSET_LOOPBACK      (1)
#define RIL_HEADSET_LOOPBACK      (2)
#define RIL_BLUETOOTH_LOOPBACK    (3)
#define RIL_MELODY_LOOPBACK       (4)

DWORD DF_LOOPBACK_DEVICES[] = {
RIL_HANDSET_LOOPBACK,      //DF_RECEIVER_LOOPBACK
RIL_HEADSET_LOOPBACK,      //DF_EARPHONE_LOOPBACK
RIL_BLUETOOTH_LOOPBACK,    //DF_BLUETOOTH_LOOPBACK
RIL_MELODY_LOOPBACK        //DF_MELODY_LOOPBACK
};
#define DF_LOOPBACK_DEVICE_MAXSIZE  (sizeof(DF_LOOPBACK_DEVICES)/sizeof(*DF_LOOPBACK_DEVICES))

#define DF_RECEIVER_LOOPBACK      (0)
#define DF_EARPHONE_LOOPBACK      (1)
#define DF_BLUETOOTH_LOOPBACK     (2)
#define DF_MELODY_LOOPBACK        (3)
#define DF_DEFAULT_DEVICE_LOOPBACK  DF_RECEIVER_LOOPBACK

HRESULT RILDrv_CheckAudioLoop(DWORD dwParam, LPDETAILEDRILDEVSPECIFIC pDevspec)
{
    FUNCTION_TRACE(RILDrv_CheckAudioLoop);
    char szCmd[MAX_PATH] = {0};
    HRESULT hr = E_FAIL;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) 
    {
        goto Error;
    }

    if(pDevspec == NULL || pDevspec->lpData == NULL)
    {
        goto Error;
    }

    DWORD *pData = (DWORD *)(pDevspec->lpData);
    DWORD dwDevice = DF_DEFAULT_DEVICE_LOOPBACK;
    for(int i=0; i<DF_LOOPBACK_DEVICE_MAXSIZE; i++)
    {
        if(pData[0] == DF_LOOPBACK_DEVICES[i])
        {
            dwDevice = i;
            break;
        }
    }
    DWORD dwFlag = pData[1];
    if(dwFlag)
    {
        _snprintfz(szCmd, MAX_PATH, "AT+TLOOP=1,%u\r", dwDevice);
    }
    else
    {
        strncpyz(szCmd, "AT+TLOOP=0\r", MAX_PATH);              
    } 
    
    if (!QueueCmd(pHandle, szCmd, CMDOPT_IGNORERADIOOFF, APIID_DEVSPECIFIC, NULL, NULL, hr) ) 
    {
        goto Error;
    }
    QueueCmdIgnoreRsp(APIID_NONE, "AT+VGR=10\r", CMDOPT_IGNORERADIOOFF, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0);

Error:
    return hr;
}

//
//
HRESULT RILDrv_DevSpecific(
    DWORD dwParam,
    const BYTE* lpbParams,              // @parm parameters for the operation to be performed
    DWORD dwSize                        // @parm size of the data pointed to by <p lpParams> in bytes
)
{
    FUNCTION_TRACE(RILDrv_DevSpecific);
#if defined (OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
    DWORD dwDevSpecificCommand;
    LPDETAILEDRILDEVSPECIFIC pDetailedDevSpecific;
    CRilHandle*  pCRilHandle;
    DWORD dwResult;

    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpbParams) {
        hr = E_FAIL;
        goto Error;
    }

    DEBUGCHK(NULL != lpbParams);
    DEBUGCHK(dwSize > 0);

    dwDevSpecificCommand = *(DWORD*)lpbParams;
    if ( dwSize == sizeof(DETAILEDRILDEVSPECIFIC) )
    {
        pDetailedDevSpecific = (LPDETAILEDRILDEVSPECIFIC)lpbParams;
        dwDevSpecificCommand = pDetailedDevSpecific->dwDevSpecificCommand;
    }
    else if (dwSize != sizeof(DWORD))
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    switch ( dwDevSpecificCommand )
    {   
#if defined(WAVECOM_DRIVER)
        case HCIT_CHECK_LED_CMD:
            hr = RILDrv_CheckLed(dwParam,pDetailedDevSpecific);
            break;
        case HCIT_SET_LOOPBACK_CMD:
            hr = RILDrv_CheckAudioLoop(dwParam, pDetailedDevSpecific);
            break;
        case CHECK_BATTERY_CMD:
            hr = RILDrv_CheckBattery(dwParam,pDetailedDevSpecific);
            break;
        case CTRL_MSM_AUDIO_CMD:
            hr = RILDrv_CtrlAudio(dwParam, pDetailedDevSpecific);
            break;
        case SIM_LOCK_NETWORK_COUNTS:
            hr = RILDrv_NETWORKCOUNTS(dwParam, pDetailedDevSpecific);
            break;
#ifdef RIL_ENABLE_AT_CHANNEL_FOR_ATCI
        case FORWARD_ATCMD:
            RETAILMSG(MSG_ON, (TEXT("Invoking RILDrv_ForwardATCmd\r\n")));
            hr = RILDrv_ForwardATCmd(dwParam, pDetailedDevSpecific);
            break;
#endif
#endif        
        case RIL_DEVSPECIFICPARAM_SIMULATE_NW_DEACT:
        {
            pCRilHandle = pHandle->GetDevice();
            pCRilHandle->BroadcastRealBlobNotification(RIL_NOTIFY_GPRSCONNECTIONSTATUS, (LPDETAILEDRILDEVSPECIFIC)pDetailedDevSpecific->lpData, sizeof(RILGPRSCONTEXTACTIVATED));
            hr = pCRilHandle->GetNextCmdID();
            dwResult = 0;
            pHandle->Notify(  RIL_RESULT_OK,
                              hr,
                              &dwResult,
                              sizeof(dwResult));
        }
        break;

//#ifndef EMP_DRIVER
#if !defined(EMP_DRIVER) && !defined(WAVECOM_DRIVER)
        // EMP does not support this flow control
        case RIL_DEVSPECIFICPARAM_FLOWCONTROL:
        {
            pCRilHandle = pHandle->GetDevice();
            if ( pCRilHandle )
            {
                hr = pCRilHandle->GetNextCmdID();
                dwResult = pCRilHandle->CRilNdis.GetRadioXon();
                pHandle->Notify(  RIL_RESULT_OK,
                                  hr,
                                  &dwResult,
                                  sizeof(dwResult));
            }
            else
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;
#endif

#ifdef OEM1_DRIVER
        case RIL_DEVSPECIFICPARAM_BUILDINFO:
        {
            // HW-SPECIFIC: This uses a AT$V0 to get the firmware build number
            if (!QueueCmd(pHandle, "AT$V0\r", CMDOPT_NONE, APIID_DEVSPECIFIC, ParseBuildInfo,
                          NULL, hr)) {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;

        case RIL_DEVSPECIFICPARAM_BATTERYINFO:
        {
            if(!QueueCmd(pHandle, "AT%A0\r", CMDOPT_IGNORERADIOOFF | CMDOPT_SUPPRESSLOGGING, APIID_DEVSPECIFIC, ParseBatteryInfo, NULL, hr))
            {
                hr = E_FAIL;
                goto Error;
            }
        }
        break;
#endif
#ifdef SIMULATE_REBOOT_RADIO
        case RIL_DEVSPECIFICPARAM_REBOOTRADIO:
        {
            hr = RILDrv_RebootRadio(dwParam);
        }
        break;
#endif // SIMULATE_REBOOT_RADIO
#ifdef SIMULATE_HUNG_RADIO
        case RIL_DEVSPECIFICPARAM_SIMULATEHUNGRADIO:
        {
            g_fSimulateHungRadio = TRUE;
        }
        break;
#endif // SIMULATE_HUNG_RADIO
        default:
        {
            hr = E_NOTIMPL;
            goto Error;
        }
    }

Error:
#else
    HRESULT hr = E_NOTIMPL;
#endif
    return hr;
}

HRESULT  RILDrv_NETWORKCOUNTS(DWORD dwParam, LPDETAILEDRILDEVSPECIFIC pDetailedDevSpecific)
{
     RETAILMSG(1, (TEXT( "RILDrv_NETWORKCOUNTS \r\n")));
     FUNCTION_TRACE(RILDrv_NETWORKCOUNTS);
     HRESULT hr = E_FAIL;

     CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
     if (!pHandle) 
     {
           hr = E_FAIL;
           goto Error;
     }
	 
     if(!QueueCmd(pHandle, "AT+CSLKC?\r", CMDOPT_NONE, APIID_DEVSPECIFIC, ParseSimLockCounts, NULL, hr))
    { 
          hr = E_FAIL;
          goto Error;
    }
	
Error:
    return hr;
}

HRESULT ParseSimLockCounts(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{RETAILMSG(1,(TEXT("chen jianjun: goto ParseSimLockCounts\r\n")));
FUNCTION_TRACE(ParseSimLockCounts);
    UINT nValue;
    DWORD* pdwCounts = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwCounts = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwCounts)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwCounts, 0x00, sizeof(DWORD));

	  // Parse "<prefix><command><list><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CSLKC: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)  ||
        !ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }
	  

   *pdwCounts = (DWORD)(nValue);
    pBlob = (void*)pdwCounts;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwCounts);
    }
    return hr; 
}

//
//
//
static HRESULT ParseGetCurrentSystemType(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetCurrentSystemType);
    DWORD* pdwSystemType = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pdwSystemType = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwSystemType)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwSystemType, 0x00, sizeof(DWORD));
#if defined(EMP_DRIVER) || defined(WAVECOM_DRIVER)
    extern DWORD g_dwSystemType;
    *pdwSystemType = g_dwSystemType;
#else
    *pdwSystemType = RIL_SYSTEMTYPE_GSM;
#endif

    pBlob = (void*)pdwSystemType;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwSystemType);
    }
    return hr;
}


//
//
//
HRESULT RILDrv_GetCurrentSystemType(DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetCurrentSystemType);
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(pHandle, "AT\r", CMDOPT_NONE, APIID_GETCURRENTSYSTEMTYPE, ParseGetCurrentSystemType, NULL, hr))
    {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
//
//
HRESULT RILDrv_LogEventToRadio(DWORD dwParam, LPCSTR szLog)
{
    FUNCTION_TRACE(RILDrv_LogEventToRadio);
    HRESULT  hr = S_OK;

#ifdef RIL_SUPPORT_LOGEVENTTORADIO
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !szLog || !*szLog) {
        hr = E_FAIL;
        goto Error;
    }

    LPCSTR   szCmdPrefix = "AT%EVENTLOG=";         // temporary, until we got a real command from radio vendor

    DWORD   szCmdLen = strlen( szCmdPrefix ) + strlen( szLog ) + 2;
    char   *szCmd = new char[szCmdLen];
    if ( szCmd )
    {
        (void)_snprintfz(szCmd, szCmdLen, "%s%s\r", szCmdPrefix, szLog );

        if (!QueueCmd(pHandle, szCmd, CMDOPT_IGNORERSP, APIID_LOGEVENTTORADIO, NULL, NULL, hr)) {
            hr = E_FAIL;
         }
    }
    else
    {
        hr = E_FAIL;
    }

Error:

    if (szCmd)
        delete [] szCmd;

#else   // RIL_SUPPORT_LOGEVENTTORADIO

    hr = E_NOTIMPL;

#endif

    return hr;
}
//sunrenhong poting for orange battery
/*===========================================================================
    FUNCTION: RILDrv_CheckBattery  

    DESCRIPTION:
        Call the  function to send AT COMMAND to QCT6280  in order to get battery voltage

    PROTOTYPE:
        HRESULT RILDrv_CheckBattery(DWORD dwParam,LPDETAILEDRILDEVSPECIFIC devspsec)

    PARAMETERS:
        dwParam:[IN]  
        devspsec:[IN]  pointer to DETAILEDRILDEVSPECIFIC entery.

        typedef struct detailedrildevspecific_tag {
            DWORD cbSize;                       // @field structure size in bytes
            DWORD dwDevSpecificCommand;         // one of the RIL_DEVSPECIFICPARAM_ that require data
            PVOID lpData;                      // pointer to data specific to dwDevSpecificCommand
        } DETAILEDRILDEVSPECIFIC, *LPDETAILEDRILDEVSPECIFIC;

    RETURN VALUE
        none
===========================================================================*/

HRESULT RILDrv_CheckBattery(DWORD dwParam,LPDETAILEDRILDEVSPECIFIC  devspsec)
{
    RETAILMSG(MSG_ON, (TEXT( "RILDrv_GetBatteryStatus \r\n")));
    FUNCTION_TRACE(RILDrv_CheckBattery);
    HRESULT hr = E_FAIL;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) 
    {
        hr = E_FAIL;
        goto Error;
    }
    RETAILMSG(MSG_ON, (TEXT("cbsize: %x dwDevSpecificCommand: %x\r\n"), (DWORD)(devspsec->cbSize), (DWORD)(devspsec->dwDevSpecificCommand)));
    if (!QueueCmd(pHandle, "AT+CBC\r", CMDOPT_IGNORERADIOOFF, APIID_DEVSPECIFIC, ParseCheckBattery, NULL, hr)) 
    {
        hr = E_FAIL;
        goto Error;
    }
Error:
    return hr;
}
static HRESULT ParseCheckBattery(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    UINT bcs = 0;
    UINT bcl = 0;
    UINT* pdwIndex = NULL;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    pdwIndex = (UINT*)AllocBlob(sizeof(UINT)*2);
    if (!pdwIndex)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwIndex, 0x00, sizeof(DWORD));

    // Parse "<prefix>+CBC: <bcs>,<bcl><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "+CBC: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, bcs, szRsp)        ||
        !MatchStringBeginning(szRsp, ",", szRsp)||
        !ParseUInt(szRsp, TRUE, bcl, szRsp)        ||
        !ParseRspPostfix(szRsp, szRsp))
    {
        RETAILMSG(MSG_ON,(TEXT("!!!!!!!!!!!!!!!!!!!!!!!!!! \r\n")));  
        goto Error;
    }

    *pdwIndex = bcs;
    *(pdwIndex+1) = bcl;
    pBlob = (void*)pdwIndex;
    cbBlob = sizeof(UINT)*2;
    hr = S_OK;

Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwIndex);
    }
    return hr;
}//sunrenhong end

#ifdef RIL_ENABLE_TIMESYNC
DWORD g_dwFirstPowerON = 0;
FILETIME g_ftDefault;
FILETIME g_ftMinimum;
FILETIME g_ftMaximum;
SYSTEMTIME g_stDefault;
SYSTEMTIME g_stMinimum;
SYSTEMTIME g_stMaximum;

HRESULT PrintFileTime(const FILETIME *pFTime , LPCSTR szRemark)
{
    HRESULT hr = S_OK;
    BYTE *pWalk = NULL;
    BYTE *pEnd = NULL;
    CHAR szPrtBuf[MAX_PATH] = {0};
    CHAR *pOut = szPrtBuf;
    if(pFTime == NULL)
    {
        hr = E_FAIL;
        goto Error;
    }
    pWalk = (BYTE *)pFTime;
    pEnd = pWalk + sizeof(*pFTime);
    while(pWalk < pEnd)
    {
        (void)_snprintfz(pOut, MAX_PATH-(pOut-szPrtBuf), "%02X", *pWalk);
        pWalk++;
        pOut+=2;
    }
    if(szRemark)
    {
        RETAILMSG(MSG_ON, (TEXT("%a : FILETIME [%a]\r\n"), szRemark, szPrtBuf));
    }
    else
    {
        RETAILMSG(MSG_ON, (TEXT("FILETIME [%a]\r\n"), szPrtBuf));
    }
Error:
    return hr;
}

HRESULT PrintSystemTime(const SYSTEMTIME *pSTime , LPCSTR szRemark)
{
    HRESULT hr = S_OK;
    CHAR szPrtBuf[MAX_PATH] = {0};
    if(pSTime == NULL)
    {
        hr = E_FAIL;
        goto Error;
    }
    (void)_snprintfz(szPrtBuf, MAX_PATH, "%04d/%02d/%02d,%02d:%02d:%02d:%03d", 
            pSTime->wYear, pSTime->wMonth, pSTime->wDay, 
            pSTime->wHour, pSTime->wMinute, pSTime->wSecond, 
            pSTime->wMilliseconds);
    if(szRemark)
    {
        RETAILMSG(MSG_ON, (TEXT("%a : SYSTEMTIME [%a]\r\n"), szRemark, szPrtBuf));
    }
    else
    {
        RETAILMSG(MSG_ON, (TEXT("SYSTEMTIME [%a]\r\n"), szPrtBuf));
    }
Error:
    return hr;
}

HRESULT PrintTimeZoneInfo()
{
    TIME_ZONE_INFORMATION tzi;
    memset(&tzi, 0, sizeof(tzi));
    DWORD dwRes = GetTimeZoneInformation(&tzi);
    if( TIME_ZONE_ID_STANDARD == dwRes )
    {
        RETAILMSG(MSG_ON, (TEXT("TimeZoneInfo: TIME_ZONE_ID_STANDARD\r\n")));
    }
    else if( TIME_ZONE_ID_DAYLIGHT == dwRes )
    {
        RETAILMSG(MSG_ON, (TEXT("TimeZoneInfo: TIME_ZONE_ID_DAYLIGHT\r\n")));
    }
    else
    {
        RETAILMSG(MSG_ON, (TEXT("TimeZoneInfo: TIME_ZONE_ID_UNKNOWN\r\n")));
    }
    RETAILMSG(MSG_ON, (TEXT("Bias: %d\r\n"), tzi.Bias));
    RETAILMSG(MSG_ON, (TEXT("StandardName: %s\r\n"), tzi.StandardName));
    PrintSystemTime(&tzi.StandardDate, "StandardDate");
    RETAILMSG(MSG_ON, (TEXT("StandardBias: %d\r\n"), tzi.StandardBias));
    RETAILMSG(MSG_ON, (TEXT("DaylightName: %s\r\n"), tzi.DaylightName));
    PrintSystemTime(&tzi.DaylightDate, "DaylightDate");
    RETAILMSG(MSG_ON, (TEXT("DaylightBias: %d\r\n"), tzi.DaylightBias));
    return S_OK;
}

BOOL SetSystemTimeToDevice( const SYSTEMTIME *pSTime , BOOL fSetModem, BOOL fSetPPC)
{
    BOOL fRet = TRUE;
    CHAR szcmd[MAX_PATH] = {0};
    
    if(pSTime == NULL)
    {
        RETAILMSG(MSG_ON, (TEXT("SetSystemTimeToDevice failed!\r\n")));
        fRet = FALSE;
        goto Error;
    }

    if(fSetModem)
    {
        (void)_snprintfz( szcmd, MAX_PATH, "AT+CCLK=\"%02d/%02d/%02d,%02d:%02d:%02d+00\"\r",
                         pSTime->wYear%100, pSTime->wMonth, pSTime->wDay,
                         pSTime->wHour, pSTime->wMinute, pSTime->wSecond );
        
        if(!QueueCmdIgnoreRsp(APIID_NONE, szcmd, CMDOPT_IGNORERADIOOFF, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0))
        {
            RETAILMSG(MSG_ON, (TEXT("Fail to set time to modem!\r\n")));
            fRet = FALSE;
            goto Error;
        }
    }
    if(fSetPPC)
    {
        if(!SetLocalTime(pSTime))
        {
            RETAILMSG(MSG_ON, (TEXT("Fail to set time to PPC!\r\n")));
            fRet = FALSE;
            goto Error;
        }
    }

Error:    
    return fRet;
}

BOOL SetFileTimeToDevice( const FILETIME *pFTime , BOOL fSetModem, BOOL fSetPPC)
{
    BOOL fRet = TRUE;
    SYSTEMTIME st;
    memset(&st, 0, sizeof(st));
    
    if(pFTime == NULL || !FileTimeToSystemTime(pFTime, &st))
    {
        RETAILMSG(MSG_ON, (TEXT("SetFileTimeToDevice failed!\r\n")));
        fRet = FALSE;
        goto Error;
    }

    fRet = SetSystemTimeToDevice(&st, fSetModem, fSetPPC);

Error:    
    return fRet;
}

void WINAPI TimeSyncCallBack(HREGNOTIFY hregNotify, DWORD dwUserData, const PBYTE pData, const UINT cbData)
{
    SYSTEMTIME st;
    memset(&st, 0, sizeof(st));
    FILETIME ft;
    memset(&ft , 0, sizeof(ft));
    GetLocalTime(&st);
    SystemTimeToFileTime(&st, &ft);
    if( CompareFileTime(&ft, &g_ftMinimum) < 0 || CompareFileTime(&ft, &g_ftMaximum) >= 0 )
    {
        //set minimum time to both modem and PPC
        //PrintSystemTime(&g_stMinimum, "Set device time");
        SetSystemTimeToDevice(&g_stMinimum, TRUE, TRUE);
    }
    else
    {
        //set current time to modem only
        //PrintSystemTime(&st, "Set device time");
        SetSystemTimeToDevice(&st, TRUE, FALSE);
    }

    return;
}

void StartTimeMonitor()
{
    HRESULT hr = S_OK;
    HREGNOTIFY hRegNotify = NULL;
    RETAILMSG(MSG_ON, (TEXT("[+%a]\r\n"), __FUNCTION__));
    hr = RegistryNotifyCallback(
                 STATE_TIME_ROOT,
                 STATE_TIME_PATH,
                 STATE_TIME_VALUE,
                 TimeSyncCallBack,
                 0,
                 NULL,
                 &hRegNotify);
    
    if ( FAILED(hr) )
    {
        hRegNotify = NULL;
        RETAILMSG(MSG_ON, (TEXT("RegistryNotifyCallback:Error!\r\n")));
    }
    RETAILMSG(MSG_ON, (TEXT("[-%a]\r\n"), __FUNCTION__));
    return;
}

void WINAPI UserFormatCallBack(HREGNOTIFY hregNotify, DWORD dwUserData, const PBYTE pData, const UINT cbData)
{
    //When user format the device, set default time to both modem and PPC
    //PrintSystemTime(&g_stDefault, "Set device time");
    SetSystemTimeToDevice(&g_stDefault, TRUE, TRUE);
    return;
}
void StartFormatMonitor()
{
    HRESULT hr = S_OK;
    HREGNOTIFY hRegNotify = NULL;
    RETAILMSG(MSG_ON, (TEXT("[+%a]\r\n"), __FUNCTION__));
    hr = RegistryNotifyCallback(
                 FORMAT_UPDATE_ROOT,
                 FORMAT_UPDATE_PATH,
                 FORMAT_UPDATE_VALUE,
                 UserFormatCallBack,
                 0,
                 NULL,
                 &hRegNotify);
    
    if ( FAILED(hr) )
    {
        hRegNotify = NULL;
        RETAILMSG(MSG_ON, (TEXT("RegistryNotifyCallback:Error!\r\n")));
    }
    RETAILMSG(MSG_ON, (TEXT("[-%a]\r\n"), __FUNCTION__));
    return;
}

HRESULT ParseGetRTC(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    HRESULT hr = S_OK;
    UINT dwY = 0;
    UINT dwM = 0;
    UINT dwD = 0;
    UINT dwH = 0;
    UINT dwMin = 0;
    UINT dwS = 0;
    SYSTEMTIME st;
    memset(&st,0,sizeof(st));
    FILETIME ft;
    memset(&ft,0,sizeof(ft));
    
    // parse <prefix>+CCLK: "<Y>/<M>/<D>,<H>:<M>:<S>"<postfix>
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp,"+CCLK: \"",szRsp) ||
        !ParseUInt(szRsp,TRUE, dwY,szRsp) ||
        !MatchStringBeginning(szRsp,"/",szRsp) ||
        !ParseUInt(szRsp,TRUE, dwM,szRsp) ||
        !MatchStringBeginning(szRsp,"/",szRsp) ||
        !ParseUInt(szRsp,TRUE, dwD,szRsp) ||
        !MatchStringBeginning(szRsp,",",szRsp) ||
        !ParseUInt(szRsp,TRUE, dwH,szRsp) ||
        !MatchStringBeginning(szRsp,":",szRsp) ||
        !ParseUInt(szRsp,TRUE, dwMin,szRsp) ||
        !MatchStringBeginning(szRsp,":",szRsp) ||
        !ParseUInt(szRsp,TRUE, dwS,szRsp) ||
        !FindRspPostfix(szRsp, szRsp)  )
    {
        hr = E_FAIL;
        goto Error;
    }

    if( dwY >= g_stMinimum.wYear%100u )
    {
        dwY += g_stMinimum.wYear-(g_stMinimum.wYear%100u);
    }
    else
    {
        dwY += g_stMaximum.wYear-(g_stMaximum.wYear%100u);
    }
    st.wYear = (WORD)(dwY);
    st.wMonth = (WORD)(dwM);
    st.wDay = WORD(dwD);
    st.wHour = WORD(dwH);
    st.wMinute = (WORD)(dwMin);
    st.wSecond = (WORD)(dwS);

    if(!SystemTimeToFileTime(&st, &ft))
    {
        hr = E_FAIL;
        goto Error;
    }

    if(CompareFileTime(&ft, &g_ftMinimum) < 0 || CompareFileTime(&ft, &g_ftMaximum) >= 0 )
    {
        //set default time to both modem and PPC
        //PrintSystemTime(&g_stDefault, "Set device time");
        if(!SetSystemTimeToDevice(&g_stDefault, TRUE, TRUE))
        {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        //set current time to PPC only
        //PrintSystemTime(&st, "Set device time");
        if( !SetSystemTimeToDevice(&st, FALSE, TRUE))
        {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}

BOOL StartTimeSync()
{
    BOOL fRet = TRUE;
    BYTE biDefValue[] = {0x00, 0x00, 0xB0, 0xC7, 0x37, 0x2D, 0xC7, 0x01};
    BYTE biMinValue[] = {0x00, 0x80, 0x51, 0x0B, 0x1B, 0xA2, 0xAF, 0x01};
    BYTE biMaxValue[] = {0x00, 0x40, 0x48, 0x5D, 0x93, 0xBF, 0x1F, 0x02};

    GetRegistryDWORD(RILRTC_CONTROL_ROOT, RILRTC_CONTROL_PATH, RILRTC_CONTROL_FIRSTPOWERON, &g_dwFirstPowerON);
    if(!GetRegistryBINARY(RILRTC_CONTROL_ROOT, RILRTC_CONTROL_PATH, RILRTC_CONTROL_DEFAULTTIME, (PBYTE)&g_ftDefault, sizeof(g_ftDefault)))
    {
        RETAILMSG(MSG_ON, (TEXT("Can't get default time from registry.\r\n")));
        memcpy(&g_ftDefault, biDefValue, sizeof(g_ftDefault));
    }
    if(!GetRegistryBINARY(RILRTC_CONTROL_ROOT, RILRTC_CONTROL_PATH, RILRTC_CONTROL_MINIMUMTIME, (PBYTE)&g_ftMinimum, sizeof(g_ftMinimum)))
    {
        RETAILMSG(MSG_ON, (TEXT("Can't get minimum time from registry.\r\n")));
        memcpy(&g_ftMinimum, biMinValue, sizeof(g_ftMinimum));
    }
    if(!GetRegistryBINARY(RILRTC_CONTROL_ROOT, RILRTC_CONTROL_PATH, RILRTC_CONTROL_MAXIMUMTIME, (PBYTE)&g_ftMaximum, sizeof(g_ftMaximum)))
    {
        RETAILMSG(MSG_ON, (TEXT("Can't get maximum time from registry.\r\n")));
        memcpy(&g_ftMaximum, biMaxValue, sizeof(g_ftMaximum));
    }

    FileTimeToSystemTime(&g_ftDefault, &g_stDefault);
    FileTimeToSystemTime(&g_ftMinimum, &g_stMinimum);
    FileTimeToSystemTime(&g_ftMaximum, &g_stMaximum);
    PrintSystemTime(&g_stDefault, "DefaultTime");
    PrintSystemTime(&g_stMinimum, "MinimumTime");
    PrintSystemTime(&g_stMaximum, "MaximumTime");
    PrintTimeZoneInfo();
    
    if( g_dwFirstPowerON )
    {
        //If this is the first time to power on, set default time to device
        //PrintSystemTime(&g_stDefault, "Set device time");
        if(!SetSystemTimeToDevice(&g_stDefault, TRUE, TRUE))
        {
            fRet = FALSE;
            goto Error;
        }
        if(!SetRegistryDWORD(RILRTC_CONTROL_ROOT, RILRTC_CONTROL_PATH, RILRTC_CONTROL_FIRSTPOWERON, 0))
        {
            fRet = FALSE;
            goto Error;
        }
    }
    else
    {
        //if this is not the first time to power on, device get system time from modem
        if(!QueueCmdIgnoreRsp(APIID_NONE, "AT+CCLK?\r", CMDOPT_IGNORERADIOOFF, g_TimeoutCmdInit, ParseGetRTC, NULL, 0, 0, 0))
        {
            fRet = FALSE;
            goto Error;
        }
    }

    StartTimeMonitor();
    StartFormatMonitor();

Error:
    return fRet;
}
#endif //WAVECOM_DRIVER

#ifdef RIL_ENABLE_AT_CHANNEL_FOR_ATCI
HRESULT RILDrv_ForwardATCmd(DWORD dwParam, LPDETAILEDRILDEVSPECIFIC pDevSpec)
{
    HRESULT hRet = E_FAIL;
    LPCSTR szCmd = NULL;
    DWORD dwOptions = 0;
    
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if ( !pHandle || !pDevSpec) 
    {
        goto Error;
    }
    szCmd = (LPCSTR)(pDevSpec->lpData);
    dwOptions = CMDOPT_FORCEPARSE | CMDOPT_IGNORERADIOOFF; 
    if( !strcmp("AT***\r", szCmd) )
    {
        g_dwATCI = dwParam;
        dwOptions |= CMDOPT_NOOP;
    }
    else if( !strcmp("AT###\r", szCmd) )
    {
        g_dwATCI = 0;
        g_fATCILogOn = FALSE;
        dwOptions |= CMDOPT_NOOP;
    }
    else if( !strcmp("AT*LOGON\r", szCmd) )
    {
        g_fATCILogOn = TRUE;
        dwOptions |= CMDOPT_NOOP;
    }
    else if( !strcmp("AT*LOGOFF\r", szCmd) )
    {
        g_fATCILogOn = FALSE;
        dwOptions |= CMDOPT_NOOP;
    }
    
    RETAILMSG(MSG_ON, (TEXT("[RILDrv_ForwardATCmd]Queue command with pHandle: 0x%x\r\n"), pHandle));
    QueueCmd(pHandle, szCmd, dwOptions, APIID_DEVSPECIFIC, ParserForwardATCmd , NULL, hRet);
Error:
    return hRet;
}

HRESULT ParserForwardATCmd(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    DWORD dwRspLen = 1;
    HRESULT hr = S_OK;
    pBlob = NULL;
    cbBlob = 0;
    if(szRsp)
    {
        dwRspLen += strlen(szRsp);
    }
    pBlob = AllocBlob(dwRspLen);
    if(!pBlob)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pBlob, 0, dwRspLen);
    if(szRsp)
    {
        _snprintfz((char *)pBlob, dwRspLen, szRsp);
    }
    cbBlob = dwRspLen;

Error:
    if (FAILED(hr)) {
        FreeBlob(pBlob);
    }
    return hr;
}
#endif


/*===========================================================================
	FUNCTION: RILDrv_CtrlAudio  

	DESCRIPTION:
		Call the  function to send AT COMMAND to Dragonfly in order to ctrl AUDIO

	PROTOTYPE:
		HRESULT RILDrv_CtrlAudio(DWORD dwParam,LPDETAILEDRILDEVSPECIFIC  devspsec)

	PARAMETERS:
	    dwParam:[IN]  
		devspsec:[IN]  pointer to DETAILEDRILDEVSPECIFIC entery.
		
		typedef struct detailedrildevspecific_tag {
		    DWORD cbSize;                       // @field structure size in bytes
		    DWORD dwDevSpecificCommand;         // one of the RIL_DEVSPECIFICPARAM_ that require data
		    PVOID lpData;                       // pointer to data specific to dwDevSpecificCommand
		} DETAILEDRILDEVSPECIFIC, *LPDETAILEDRILDEVSPECIFIC;
		
	RETURN VALUE
		none
===========================================================================*/
#define  CHANNEL_FOR_RECEIVER     0
#define  CHANNEL_FOR_HEADSET      1
#define  CHANNEL_FOR_HANDFREE     2
#define  CHANNEL_FOR_BLUETOOTH    3
#define  CHANNEL_FOR_VOIP         4
#define  CHANNEL_FOR_CARKIT_MUSIC 5
#define  CHANNEL_FOR_CARKIT_VOICE 6
HRESULT RILDrv_CtrlAudio(DWORD dwParam, LPDETAILEDRILDEVSPECIFIC devspsec)
{
    RETAILMSG(MSG_ON, (TEXT( "RILDrv_CtrlAudio\r\n")));
    FUNCTION_TRACE(RILDrv_CtrlAudio);
    char szCmd[MAX_PATH] = {0};
    HRESULT hr = E_FAIL;
    DWORD dwValue = 0;
    DWORD dwOption = CMDOPT_NONE;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || devspsec->lpData==NULL)
    {
        goto Error;
    }
    AUDIOCTRLINFO * p = ( AUDIOCTRLINFO * )(devspsec->lpData);
    switch(p->porttype)
    {
        case CHANNEL_FOR_RECEIVER:
            dwValue = 0;
            break;
        case CHANNEL_FOR_HEADSET:
        case CHANNEL_FOR_VOIP:
        case CHANNEL_FOR_CARKIT_MUSIC:
        case CHANNEL_FOR_CARKIT_VOICE:
            dwValue = 1;
            break;
        case CHANNEL_FOR_HANDFREE:
            dwValue = 2;
            break;
        case CHANNEL_FOR_BLUETOOTH:
            dwValue = 3;
            break;
        default:
            goto Error;
    }
    dwOption = (p->status)? (CMDOPT_NONE) : (CMDOPT_NOOP);
        
    //(void)_snprintfz(szCmd, MAX_PATH, "AT+AUDIO=%u,%u\r", p->porttype,p->status);
    (void)_snprintfz(szCmd, MAX_PATH, "AT+SPEAKER=%u\r",dwValue);
    if (!QueueCmd(pHandle, szCmd, dwOption, APIID_DEVSPECIFIC, NULL, NULL, hr)) 
    {
        goto Error;
    }
   
Error:
    return hr;
}


#define PA_P0_DEFAULT  (12)
#define PA_P1_DEFAULT  (13)
#define PA_P2_DEFAULT  (15)
#define WLAN_DEPRESSED (6)

BOOL DepressWLAN()
{
    BOOL fRet = FALSE;
    HANDLE hnd = NULL;
	PNDISUIO_SET_OID pSetOid;
	SYC_RF_PA_CONFIG sConfig;
	DWORD dwBytesReturned=0;
    
    hnd = CreateFile(   TEXT("UIO1:"),                   //Object name.
                        GENERIC_READ | GENERIC_WRITE,    //Desired access.
                        FILE_SHARE_READ | FILE_SHARE_WRITE,         //Share Mode.
                        NULL,                                       //Security Attr
                        OPEN_EXISTING,                              //Creation Disposition.
                        FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //Flag and Attributes.
                        (HANDLE)INVALID_HANDLE_VALUE);
    if( !hnd )
    {
        RETAILMSG(MSG_ON, (TEXT("[ParseNetworkType]Fail to open UIO1: \r\n")));
        goto Error;
    }

    RETAILMSG(MSG_ON, (TEXT("[ParseNetworkType]To depress RF for WLAN\r\n")));
    UCHAR SetBuffer[sizeof(NDISUIO_SET_OID) +	sizeof(SYC_RF_PA_CONFIG)];

	pSetOid = (PNDISUIO_SET_OID) &SetBuffer[0];
	pSetOid->ptcDeviceName = TEXT("CHEETAHGSP1");
	pSetOid->Oid = OID_OEM_SYCP_RF_PA_CFG;

    sConfig.EnablePA = 1; 
    sConfig.PA_P0 = WLAN_DEPRESSED;
    sConfig.PA_P1 = WLAN_DEPRESSED;
    sConfig.PA_P2 = WLAN_DEPRESSED;
    
    memcpy(&pSetOid->Data[0], &sConfig, sizeof(SYC_RF_PA_CONFIG));
    RETAILMSG(MSG_ON, ( TEXT("[ParseNetworkType]set SYC_RF_PA_CONFIG: %d,%d,%d,%d \r\n"), 
                    sConfig.EnablePA, sConfig.PA_P0, sConfig.PA_P1, sConfig.PA_P2 ));

    if(!DeviceIoControl(hnd,
			IOCTL_NDISUIO_SET_OID_VALUE,
			(LPVOID) &SetBuffer[0],
			sizeof(SetBuffer),
			(LPVOID) &SetBuffer[0],
			0,
			&dwBytesReturned,
			NULL))
    {
        RETAILMSG(MSG_ON, (TEXT("[ParseNetworkType]Fail to depress RF for WLAN\r\n")));
    }

    fRet = TRUE;
Error:
    if(hnd)
    {
        CloseHandle( hnd );
        hnd = NULL;
    }
    return fRet;
}

BOOL RestoreWLAN()
{
    BOOL fRet = FALSE;
    HANDLE hnd = NULL;
    UCHAR SetBuffer[sizeof(NDISUIO_SET_OID) +	sizeof(SYC_RF_PA_CONFIG)];
	PNDISUIO_SET_OID pSetOid;
	SYC_RF_PA_CONFIG sConfig;
	DWORD dwBytesReturned=0;
    
    hnd = CreateFile(   TEXT("UIO1:"),                   //Object name.
                        GENERIC_READ | GENERIC_WRITE,    //Desired access.
                        FILE_SHARE_READ | FILE_SHARE_WRITE,         //Share Mode.
                        NULL,                                       //Security Attr
                        OPEN_EXISTING,                              //Creation Disposition.
                        FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, //Flag and Attributes.
                        (HANDLE)INVALID_HANDLE_VALUE);
    if( !hnd )
    {
        RETAILMSG(MSG_ON, (TEXT("[ParseNetworkType]Fail to open UIO1: \r\n")));
        goto Error;
    }

    RETAILMSG(MSG_ON, (TEXT("[ParseNetworkType]To restore RF for WLAN\r\n")));
	pSetOid = (PNDISUIO_SET_OID) &SetBuffer[0];
	pSetOid->ptcDeviceName = TEXT("CHEETAHGSP1");
	pSetOid->Oid = OID_OEM_SYCP_RF_PA_CFG;
		
    sConfig.EnablePA = 1; 
    sConfig.PA_P0 = PA_P0_DEFAULT;
    sConfig.PA_P1 = PA_P1_DEFAULT;
    sConfig.PA_P2 = PA_P2_DEFAULT;
    
    memcpy(&pSetOid->Data[0], &sConfig, sizeof(SYC_RF_PA_CONFIG));
    RETAILMSG(MSG_ON, ( TEXT("[ParseNetworkType]set SYC_RF_PA_CONFIG: %d,%d,%d,%d \r\n"), 
                    sConfig.EnablePA, sConfig.PA_P0, sConfig.PA_P1, sConfig.PA_P2 ));

    if(!DeviceIoControl(hnd,
			IOCTL_NDISUIO_SET_OID_VALUE,
			(LPVOID) &SetBuffer[0],
			sizeof(SetBuffer),
			(LPVOID) &SetBuffer[0],
			0,
			&dwBytesReturned,
			NULL))
    {
        RETAILMSG(MSG_ON, (TEXT("[ParseNetworkType]Fail to restore RF for WLAN\r\n")));
    }

    fRet = TRUE;
Error:
    if(hnd)
    {
        CloseHandle( hnd );
        hnd = NULL;
    }
    return fRet;
}
/*
HRESULT ParseGetBandStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    HRESULT hRes = E_FAIL;
    LPCSTR szDummy = NULL;
    static BOOL fWLANDepressed = FALSE;
    if ( !ParseRspPrefix(szRsp, szRsp) ||
         !MatchStringBeginning(szRsp, "+CGMM: ", szRsp) ||
         !FindRspPostfix(szRsp, szDummy) )
    {
        goto Error;
    }
    
    if(MatchStringAnywhere(szRsp, "1900", szDummy))
    {
        //While PCS1900, depress WLAN RF.
        RETAILMSG(MSG_ON, (TEXT("[ParseGetBandStatus] DepressWLAN\r\n")));
        fWLANDepressed = DepressWLAN();
    }
    else if(fWLANDepressed)
    {
        //While not PCS1900 and WLAN depressed, restore WLAN RF.
        RETAILMSG(MSG_ON, (TEXT("[ParseGetBandStatus]RestoreWLAN\r\n")));
        fWLANDepressed = !RestoreWLAN();
    }
    
    hRes = S_OK;
Error:
    return hRes;
    
}

HRESULT RILDrv_GetBandStatus()
{
    RETAILMSG(MSG_ON, (TEXT( "RILDrv_GetBandStatus\r\n")));
    FUNCTION_TRACE(RILDrv_GetBandStatus);
    HRESULT hr = S_OK;
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+CGMM?\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, ParseGetBandStatus, NULL, 0, 0, 0)) 
    {
        hr = E_FAIL;
    }

    return hr;
}
*/
HRESULT ParseGetAccessTech(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    HRESULT hr = E_FAIL;
    LPCSTR szDummy = NULL;
    UINT uiMode = 0;
    UINT uiStatus = 0;
    UINT uiLAC = 0;
    UINT uiCI = 0;
   // DWORD dwRegStatus;
    UINT uiParamCount = 0;
    UINT uiParams[4] = {0, 0, 0, 0};

    // TF specific registration result format +TEDGE: <mode>,<status>[, <lac>, <ci>]
    //  <mode>   command mode: 0, 1, 2
    //  <status> registration status: 0, 1, 2, 3, 4, 5, 6(EDGE available)
    //  <lac>    quoted string in HEX format
    //  <ci>     quoted string in HEX format

    //parse "<prefix>+TEDGE: "
    if(!ParseRspPrefix(szRsp, szRsp) ||
       !MatchStringBeginning(szRsp, "*EDGE: ", szRsp) )
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : ParseGetAccessTech : Failed to parse command body.\r\n")));
        goto Error;
    }
    
    // Parse and store parameters
    if(!ParseUInt(szRsp, TRUE, uiStatus,szRsp))
    {
    	goto Error;
    }

    // We have the parameters, parse the postfix
    if (!ParseRspPostfix(szRsp, szRsp))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : E : ParseGetAccessTech : Failed to find postfix.\r\n")));
        goto Error;
    }

  

    DWORD dwSystemType, dwSystemCaps;
    if (1 == uiStatus)
    {
        dwSystemType = RIL_SYSTEMTYPE_EDGE;
        dwSystemCaps = RIL_SYSTEMCAPS_NONE;
    }
    else
    {
        dwSystemType = RIL_SYSTEMTYPE_GSM;
        dwSystemCaps = RIL_SYSTEMCAPS_NONE;
    }
    if (dwSystemType != g_dwSystemType)
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : i : ParseExtRegistrationStatus : System changed.\r\n")));
        // Update the new network type and capability
        g_fSystemChanged = TRUE;
        g_dwSystemType = dwSystemType;
        g_dwSystemCaps = dwSystemCaps;
    }
    
    hr = S_OK;

Error:
    return hr;
}

HRESULT RILDrv_GetAccessTech()
{
    RETAILMSG(MSG_ON, (TEXT( "RILDrv_GetAccessTech\r\n")));
    FUNCTION_TRACE(RILDrv_GetAccessTech);
    HRESULT hr = S_OK;
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT*EDGE\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, ParseGetAccessTech, NULL, 0, 0, 0)) 
    {
        hr = E_FAIL;
    }

    return hr;
}

#define REG_BTADDRESS_KEYNAME     TEXT("Software\\CSR\\General")
#define REG_BTADDRESS_VALUENAME   TEXT("NvBtAddress")
#define BT_ADDRESS_LENGTH   (12)
HRESULT ParseGetBTAddress(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    HRESULT hr = S_OK;
    CHAR szAddress[BT_ADDRESS_LENGTH+1] = {0};
    WCHAR wszAddress[BT_ADDRESS_LENGTH+1] = {0};
    pBlob = NULL;
    cbBlob = 0;
    
    if ( !ParseRspPrefix(szRsp, szRsp) ||
         !MatchStringBeginning(szRsp, "+TGLDA:", szRsp) ||
         !ParseUnquotedString(szRsp, '\r', szAddress, ARRAYSIZE(szAddress), szRsp) ||
         !ParseRspPostfix(szRsp, szRsp) )
    {
        hr = E_FAIL;
        goto Error;
    }
    if(!MultiByteToWideChar(CP_ACP, 0, szAddress, -1, wszAddress, ARRAYSIZE(wszAddress)))
    {
        hr = E_FAIL;
        goto Error;
    }
     if(wcscmp(wszAddress,TEXT("ffffffffffff")) != 0)
    {
         RETAILMSG(1, (TEXT( "the address is not identical to \"ffffffffffff\"\r\n")));
         if(!SetRegistrySZ(HKEY_LOCAL_MACHINE, REG_BTADDRESS_KEYNAME, REG_BTADDRESS_VALUENAME, wszAddress))
         {
            hr = E_FAIL;
            goto Error;
         }
    }
    else
    {
         RETAILMSG(1, (TEXT( "The address is identical to \"ffffffffffff\" \r\n")));
    }
 Error:
    return hr;
    
}

HRESULT RILDrv_GetBTAddress()
{
    RETAILMSG(MSG_ON, (TEXT( "RILDrv_GetBTAddress\r\n")));
    FUNCTION_TRACE(RILDrv_GetBTAddress);
    HRESULT hr = S_OK;
    if (!QueueCmdIgnoreRsp(APIID_NONE, "AT+TGLDA\r", CMDOPT_IGNORERADIOOFF, g_TimeoutAPIDefault, ParseGetBTAddress, NULL, 0, 0, 0)) 
    {
        hr = E_FAIL;
        goto Error;
    }
Error:
    return hr;
}
